How to handle two functions onClick - javascript

In my Class component App.jsx I have the following function:
import { Position } from "./Position";
...
getPositionData = val => {
const { players } = this.props;
var res = players.filter(o=>Object.values(o).includes(val))
return res
};
Which is being handled at render() when I click on a subcomponent inside a <div>, like so:
<div className="field-row">
{this.getPlayersByPosition(players, 4).map((player,i) => (
<Position key={i} handleClick={this.getPositionData}>{player.name}</Position>
))}
</div>
On its turn, Position.jsx handles onClick():
export const Position = props => (
<div
className={`position-wrapper ${
isManager(props.children) ? null : "field"
}`}
onClick={() => props.handleClick(props.children)}
>
{props.children}
</div>
);
But now I'd like to handle a second function when <div> is clicked, say:
toggleFullScreen() {
this.setState({ fullScreen: !this.state.fullScreen });
}
which I would pass, on its own, like so:
onClick={this.toggleFullScreen.bind(this)}
How do I handle BOTH functions onClick() at once in
<div className="field-row">
...
</div>
?

Just like you can invoke multiple functions at once in a JS listener with
element.addEventListener('click', () => {
fn1();
fn2();
});
You can do the same thing in React by defining the onClick to be a function which invokes both:
<Position
key={i}
getPositionData={this.getPositionData}
toggleFullScreen={toggleFullScreen}
>{player.name}</Position>
export const Position = props => (
<div
className={`position-wrapper ${
isManager(props.children) ? null : "field"
}`}
onClick={() => {
props.getPositionData(props.children);
props.toggleFullScreen();
}}
>
{props.children}
</div>
);

Related

How to test a function inside a functional component like OnFinish, and update etc

I have a component Template. Inside the Template component I defined another two functions. I want to test onFinish, updateProviderEmailTemplate functions using jest, enzyme but I don't test it. Can anyone help me how I can test those functions inside the template component. In class component I used to test those instance but I don't know how to test it using functional component.
const Template = () => {
const { isLoading, placeholderList, templateData } = useTemplateData(
new ProviderEmailTemplateService()
);
const [convertedContent, setConvertedContent] = useState(null);
const providerEmailTemplateService = new ProviderEmailTemplateService();
const updateProviderEmailTemplate = async (id, subject, content) => {
try {
const response = await providerEmailTemplateService.updateProviderEmailTemplate(
id,
subject,
content
);
notificationHelper("success", uiLabel.SUCCESS_TITLE, response.message);
} catch (error) {
notificationHelper("error", uiLabel.ERROR_TITLE, error.message);
}
};
const onFinish = async () => {
if (convertedContent === templateData.content) return;
updateProviderEmailTemplate(
templateData.id,
templateData.subject,
convertedContent
);
};
return (
<div className="template">
<Form name="basic" onFinish={onFinish}>
{templateData &&
templateData?.content &&
!isLoading &&
placeholderList.length !== 0 ? (
<>
<CustomEditor
setConvertedContent={setConvertedContent}
content={templateData.content}
placeholderList={[
<CustomEditorToolBar
optionList={[...placeholderList]}
toolBarName="Insert field"
/>,
]}
/>
</>
) : (
<LoadingSpinner isShowing={isLoading} />
)}
<Form.Item>
<div className="d-flex justify-content-end">
<Button type="primary" htmlType="submit">
Save
</Button>
</div>
</Form.Item>
</Form>
</div>
);
};
export default Template;

How to pass HTML attributes to child component in React?

I have a parent and a child component, child component has a button, which I'd like to disable it after the first click. This answer works for me in child component. However the function executed on click now exists in parent component, how could I pass the attribute down to the child component? I tried the following and it didn't work.
Parent:
const Home = () => {
let btnRef = useRef();
const handleBtnClick = () => {
if (btnRef.current) {
btnRef.current.setAttribute("disabled", "disabled");
}
}
return (
<>
<Card btnRef={btnRef} handleBtnClick={handleBtnClick} />
</>
)
}
Child:
const Card = ({btnRef, handleBtnClick}) => {
return (
<div>
<button ref={btnRef} onClick={handleBtnClick}>Click me</button>
</div>
)
}
In general, refs should be used only as a last resort in React. React is declarative by nature, so instead of the parent "making" the child disabled (which is what you are doing with the ref) it should just "say" that the child should be disabled (example below):
const Home = () => {
const [isButtonDisabled, setIsButtonDisabled] = useState(false)
const handleButtonClick = () => {
setIsButtonDisabled(true)
}
return (
<>
<Card isDisabled={isButtonDisabled} onButtonClick={handleButtonClick} />
</>
)
}
const Card = ({isDisabled, onButtonClick}) => {
return (
<div>
<button disabled={isDisabled} onClick={onButtonClick}>Click me</button>
</div>
)
}
Actually it works if you fix the typo in prop of Card component. Just rename hadnlBtnClick to handleBtnClick
You don't need to mention each prop/attribute by name as you can use javascript Object Destructuring here.
const Home = () => {
const [isButtonDisabled, setIsButtonDisabled] = useState(false)
const handleButtonClick = () => {
setIsButtonDisabled(true)
}
return (
<>
<Card isDisabled={isButtonDisabled} onButtonClick={handleButtonClick} />
</>
)
}
const Card = (props) => {
return (
<div>
<button {...props}>Click me</button>
</div>
)
}
You can also select a few props and use them differently in the child components. for example, see the text prop below.
const Home = () => {
const [isButtonDisabled, setIsButtonDisabled] = useState(false)
const handleButtonClick = () => {
setIsButtonDisabled(true)
}
return (
<>
<Card text="I'm a Card" isDisabled={isButtonDisabled} onButtonClick={handleButtonClick} />
</>
)
}
const Card = ({text, ...restProps}) => {
return (
<div>
<button {...restProps}>{text}</button>
</div>
)
}

How to test prop function that changes other prop Jest Enzyme

I have a component that receives value 'openDrawer' (bool) and function 'toggleDrawerHandler' in props, the function 'toggleDrawerHandler' changes the value of 'openDrawer' prop.
I would like to test it by simulating a click on div that triggers this function, and check if the component change when the value of 'openDrawer' changes.
The component
const NavigationMobile = (props) => {
const { openDrawer, toggleDrawerHandler } = props;
let navClass = ["Nav-Mobile"];
if (!openDrawer) navClass.push("Close");
return (
<div className="Mobile">
<div className="Menubar" onClick={toggleDrawerHandler}>
{openDrawer ? <FaTimes size="1.5rem" /> : <FaBars size="1.5rem" />}
</div>
<nav className={navClass.join(" ")} onClick={toggleDrawerHandler}>
<Navigation />
</nav>
</div>
);
};
The component that sends these props
const Header = (props) => {
const [openDrawer, setOpenDrawer] = useState(false);
const toggleDrawerHandler = () => {
setOpenDrawer((prevState) => !prevState);
};
return (
<header className="Header">
<NavigationMobile openDrawer={openDrawer} toggleDrawerHandler={toggleDrawerHandler} />
</header>
);
};
my test, but doesn't work
it("changes prop openDrawer when click", () => {
const wrapper = shallow(<NavigationMobile />);
expect(wrapper.find("FaBars")).toHaveLength(1);
expect(wrapper.find("nav").hasClass("Nav-Mobile")).toBeTruthy();
wrapper.find(".Menubar").simulate("click", true); // doesnt work
expect(wrapper.find("FaTimes")).toHaveLength(1);
expect(wrapper.find("nav").hasClass("Nav-Mobile Close")).toBeTruthy();
});

How to pass variable in a function call?

In my functional component I'm calling two functions, which are doing nearly the same thing:
const App = () => {
const handleOnClickFirst = () => {
setValue('first')
}
const handleOnClickSecond = () => {
setValue('second')
}
return (
<div>
{anything === true
? <Button onClick={handleOnClickFirst} />
: <Button onClick={handleOnClickSecond} />}
</div>
)
}
So there should be simply only
const handleOnClick = (value) => {
setValue(value)
}
But how do I pass the value in onClick?
As we know with JSX you pass a function as the event handler. So we can wrap our handler with another function and call handler with arguments in this wrapper function
onClick={(event) => handleOnClick(value)}
or for old versions we can do
onClick={function(event){ handleOnClick(value) }}
If you don't need events you can just pass it like this
onClick={() => handleOnClick(value)}
Also if it is a Class base component we use bind to pass method context and arguments
class App extends React.Component {
handleOnClick(val) {
console.log(`${val}`);
}
render() {
return (
<button onClick={this.handleOnClick.bind(this, "test")}>
click me
</button>
);
}
}
You could do
onClick={() => handleOnClick(value)}
edit:
More information here: https://reactjs.org/docs/faq-functions.html
You can do this :
<button onClick={(event)=>handleOnClick(<pass your paramter here>)}></button>
Try this:
const App = () => {
const handleOnClick = (passedInValue) => {
setValue(passedInValue)
}
return (
<div>
{anything === true
? <Button onClick={() => handleOnClick("first")} />
: <Button onClick={() => handleOnClick("second")} />}
</div>
)
}

To do list using react-sortable-hoc

I'm using sortable drag and drop which works fine. The problem is that I'd like users to be able to remove items. The SortableItem component isn't accessible as it came with the code, so I can't pass an event handler that takes index as an argument. Here's what I have so far:
const SortableItem = SortableElement(
({value}) =>
<ul>{value}</ul>
);
const SortableList = SortableContainer(({items}) => {
return (
<ul>
{items.map((value, index) => (
<SortableItem key={`item-${index}`} index={index} value={value} />
))}
</ul>
);
});
export class BlocksContainer extends React.Component {
constructor(props){
super(props);
this.state = {
items: [],
};
}
onSortEnd = ({oldIndex, newIndex}) => {
this.setState({
items: arrayMove(this.state.items, oldIndex, newIndex),
});
};
addBlock = (block) =>{
let arr = [...this.state.items, block];
this.setState({items: arr})
}
removeBlock = (index) => {
let remove = [...this.state.items];
remove.filter(block => block === index);
this.setState({items:remove})
}
render() {
return (<div><div onChange={console.log(this.state)} className="sortableContainer"><SortableList items={this.state.items} onSortEnd={this.onSortEnd} /></div>
<h2>Blocks</h2>
<button onClick={() => this.addBlock(<BannerImage remove={this.removeBlock} />)}>Banner Image</button>
<button onClick={() => this.addBlock(<ShortTextCentred remove={this.removeBlock}/>)}>Short Text Centred</button>
<h2>Layouts</h2>
<hello />
</div>
)
}
}
Since you dont have control over the events of the SortableItem component, you can wrap that component in a component you do have control over.
For example, if i wanted to add a click handler to the SortableItem, i would add it instead to the div wrapper:
const SortableList = SortableContainer(({ items }) => {
return (
<ul>
{items.map((value, index) => (
<div onClick={this.someEventHandler}>
<SortableItem key={`item-${index}`} index={index} value={value} />
</div>
))}
</ul>
);
});

Categories

Resources