In React, I can call the function in onClick in two different ways.
First Method: Arrow Function
const Modal = ({isOpen, toggle, children}) => {
<div onClick={() => toggle()}>
<div>
{children}
</div>
</div>
}
Second Method: Without brackets
const Modal = ({isOpen, toggle, children}) =>
{
return(
<div onClick={toggle}>
</div>
)
}
What is the difference between them? () => toggle() <-> toggle
The first way, onClick={() => toggle()}, creates a new function and assigns it to the onClick property. That function, when called, will call toggle with no arguments. A new function is created every time that component is rendered.
The second way, onClick={toggle}, does not create a new function, it directly assigns toggle to the onClick property. That means that when it's called, it receives any arguments that are passed (even if it doesn't expect any).
Each can be appropriate, depending on what you want to do. If toggle expects the arguments the click event will pass it, you're better off with the second way since you aren't creating a new function every time. If it doesn't, in general it's best not to set it up to receive an argument it doesn't expect.
The fact that the first way creates a new function on every render probably doesn't matter when you do this with a DOM element like a div, but suppose you're passing a function to a complex component that takes time to render and that optimizes render (avoiding re-rendering if its props don't change, via React.memo, PureComponent, shouldComponentUpdate, etc.):
return <SomeComplexComponent onSomething={() => toggle()} />
In that case, you might be best off "memoizing" the function (in this case, via useCallback or useMemo, usually) so you don't pass a new one to the component every time, so the complex component doesn't think it needs to re-render every time.
const onSomething = useCallback(() => toggle(), [toggle]);
// ...
return <SomeComplexComponent onSomething={onSomething} />
Although that still creates a new function on every render (so it can be passed into useCallback), useCallback will return the previous version of the function if toggle hasn't changed, which allows SomeComplexComponent to avoid re-rendering.
But there's no need for that when passing this to an HTML element or a simple component.
1st kind of code
onClick={toggle}
In this kind of onClick method you can call only one function inside onClick.
2nd kind of code
Suppose you need to call multiple functions inside the onClick. Then you have to use
onClick={() => toggle()}
Exmaple:-
OnClick = {()=>{
toggle1();
toggle2();
}}
And also you can pass parameters inside the function. using 2nd type like below.
Example: -
OnClick = {()=>{
person(name);
}}
In () => toggle() you are adding an anonymous function and then calling toggle function. If you want to pass in some arguments to toggle function, you can use this method. eg () => toggle(id, name..etc)
The other method simply add event handler and that function will receive event as argument.
Related
This question already has answers here:
Function inside functional component in React hooks - Performance
(8 answers)
Closed 6 months ago.
I'm currently learning react.js and I just want to ask a question on how to properly create an event handler function inside a function component.
Pardon me since most examples in the official documentation are using class for this types of components.
I have an example code below that seems to work fine, but I'm not sure if this is the correct way/best practice when creating an event handler inside a function component, since every time the state changes I assume that the handleClick function is also recreated isn't it? This affects the performance every time we re-render right?
Say for example when I add more elements inside the return statement that can also trigger an event, and each event has a corresponding event handler that is also declared in that same function component, that might be an overhead for those extreme cases 'isn't that correct?
As far as I know this is not a problem on class components because they have a separate render() method so the event handler functions are not recreated, how do we do this in function components?
function Toggle (props) {
const [isToggleOn, setToggle] = React.useState(false)
function handleClick (e) {
setToggle(() => !isToggleOn)
}
return (
<button onClick={handleClick}>
{isToggleOn ? 'ON' : 'OFF'}
</button>
)
}
If you don't want the handleClick function to be recreated on each render, you should use useCallback hook.
const handleClick = useCallback(() => {
setToggle((previousState) => !previousState)
}, [])
This way, handleClick is created only at the initial render because the useCallback's dependency array is empty.
You might also notice that I removed the usage of isToggleOn and instead use the previous state in the setToggle so your function don't need to depend on isToggleOn.
Is there any difference between both the button click event in the given component? Which is the preferred way to write?
export default class App extends Component {
doSomething = () => {
console.log('Hi');
}
render() {
return (
<Container>
<Button onClick={this.doSomething} title="Test" />
<Button onClick={() => this.doSomething()} title="Test" />
</Container>
);
}
}
When you don't need to pass the parameter, you can just use
{this.doSomething}
But if you need to pass the parameter to the function, then this will cause your method to execute immediately:
{this.doSomething(param)}
Thus, not to execute the function immediately, we need to use arrow method like you used:
{() => this.doSomething(param)}
Thus, in your case both are same. Because they are only executed when onClick is called ie. you click on the element.
Bonus:
You can still use the first way even you want to pass the parameter:
{this.doSomething(param)}
But for this, you need to define your method like this:
doSomething = (param) => () => {
console.log('Hi');
}
Furthermore, if you wish to use event object, then you may use like below:
doSomething = (param) => (event) => {
console.log('Hi');
}
Or, with the second approach ie. with arrow function:
{(event)=>this.doSomething(event,param)}
And obviously, if you are worried about performance, I would suggest not to use inline arrow function. The preferred way to use:
doSomething = (param1, param2,) => (event) => {
Misunderstanding:
Some people might find the method that pass the parameter without using inline arrow function will also work. But this is incorrect. Let me clarify on this.
When you use {this.doSomething(param)}, this function seems to work fine with its parameter. But if you change the state inside the handler, then you'll know the big difference. You'll get error maximum update depth exceeded react.
But with the same, you can avoid that error and also avoid the performance issue, you'll need to define the method like I stated before with arrow function:
doSomething = (param) => () => {
From doc
<Button onClick={() => this.doSomething()} title="Test" />
The problem with this syntax is that a different callback is created
each time the Button renders. In most cases, this is fine.
However, if this callback is passed as a prop to lower components,
those components might do an extra re-rendering.
<Button onClick={this.doSomething} title="Test" />
We generally recommend binding in the constructor or using the class
fields syntax, to avoid this sort of performance problem.
First we will look when to use both:
onClick={this.doSomething} : This is using class variable directly, but it cannot be used when we are required to pass parameters to the function. For that, the second way comes to rescue.
A way to pass parameters to this is :
onClick={this.doSomething.bind(params, this)}
onClick={() => this.doSomething()}: you can pass parameters to the function as
onClick={() => this.doSomething(param1, param2)}.
Also, an important point to note, when the component re-renders, memory is allocated for the second one every time, while the first one just uses memory reference. So, first one is better if you don't have to pass parameters in the function.
I am dynamically rendering react components as:
{map.call(this.state.text, (c, i) => (<Char
click={() => this.deleteCharHandler(i)}
charVal={c}
key={i}/>))}
Where there render method of the Char component is:
render() {
return(
<div className="char" onClick={this.props.click}>
{this.props.charVal}
</div>
)
That works and deleteCharHandler is appropriately called with the correct value of i.
However, I would like to avoid having to pass the handler function as a prop. After all, onClick of the div is simply executing the click property, which is an anonymous function calling deleteCharHandler. I tried to refactor as:
{map.call(this.state.text, (c, i) => (<Char
onClick={() => this.deleteCharHandler(i)}
charVal={c}
key={i}/>))}
Thus adding a react event listener to the Char element. And removed the onClick listener from the div in the Char component.
...And it doesn't work.
Why? Am I not just moving the onClick listener one level up?
You must note that passing any function to a React component is just passed down as a prop and not treated as a event listener. In order for the onClick to work it needs to be passed on to the DOM
Now either you can explicitly pass it on to div within Char component like do
render() {
return(
<div className="char" onClick={this.props.click}>
{this.props.charVal}
</div>
)
or you can destructure the other values and pass on the rest values using rest spread syntax. Either way you need to pass the handler to the DOM element
render() {
const {charVal, ...rest} = this.props;
return(
<div className="char" {...rest}>
{charVal}
</div>
)
The above method gives you the flexibility to pass on props required to the div element without having to mention them explicitly. However you must be careful that you aren't passing non required values to the div element
Is there any difference between both the button click event in the given component? Which is the preferred way to write?
export default class App extends Component {
doSomething = () => {
console.log('Hi');
}
render() {
return (
<Container>
<Button onClick={this.doSomething} title="Test" />
<Button onClick={() => this.doSomething()} title="Test" />
</Container>
);
}
}
When you don't need to pass the parameter, you can just use
{this.doSomething}
But if you need to pass the parameter to the function, then this will cause your method to execute immediately:
{this.doSomething(param)}
Thus, not to execute the function immediately, we need to use arrow method like you used:
{() => this.doSomething(param)}
Thus, in your case both are same. Because they are only executed when onClick is called ie. you click on the element.
Bonus:
You can still use the first way even you want to pass the parameter:
{this.doSomething(param)}
But for this, you need to define your method like this:
doSomething = (param) => () => {
console.log('Hi');
}
Furthermore, if you wish to use event object, then you may use like below:
doSomething = (param) => (event) => {
console.log('Hi');
}
Or, with the second approach ie. with arrow function:
{(event)=>this.doSomething(event,param)}
And obviously, if you are worried about performance, I would suggest not to use inline arrow function. The preferred way to use:
doSomething = (param1, param2,) => (event) => {
Misunderstanding:
Some people might find the method that pass the parameter without using inline arrow function will also work. But this is incorrect. Let me clarify on this.
When you use {this.doSomething(param)}, this function seems to work fine with its parameter. But if you change the state inside the handler, then you'll know the big difference. You'll get error maximum update depth exceeded react.
But with the same, you can avoid that error and also avoid the performance issue, you'll need to define the method like I stated before with arrow function:
doSomething = (param) => () => {
From doc
<Button onClick={() => this.doSomething()} title="Test" />
The problem with this syntax is that a different callback is created
each time the Button renders. In most cases, this is fine.
However, if this callback is passed as a prop to lower components,
those components might do an extra re-rendering.
<Button onClick={this.doSomething} title="Test" />
We generally recommend binding in the constructor or using the class
fields syntax, to avoid this sort of performance problem.
First we will look when to use both:
onClick={this.doSomething} : This is using class variable directly, but it cannot be used when we are required to pass parameters to the function. For that, the second way comes to rescue.
A way to pass parameters to this is :
onClick={this.doSomething.bind(params, this)}
onClick={() => this.doSomething()}: you can pass parameters to the function as
onClick={() => this.doSomething(param1, param2)}.
Also, an important point to note, when the component re-renders, memory is allocated for the second one every time, while the first one just uses memory reference. So, first one is better if you don't have to pass parameters in the function.
<div id="board" onClick={(event) =>
this.props.boardClicked(event)}>
I would like to pass a second argument trying something like this, but my syntax is off I think:
<div id="board"
onClick={(event, {this.props.activeCharacter}) =>
this.props.boardClicked(event, {this.props.activeCharacter})}>
Simple change would be that because you are using an arrow function it is lexically bound automatically to the react compmponent instance, so you can use this within function. Simply pass as an extra arg from within the function
<div id="board" onClick={(event) => this.props.boardClicked(event, this.props.activeCharacter)}>
Using arrow functions in event handlers in react is a bad idea though because it creates an anonymous function, that will cause this component to unnecessarily always re-render which can hurt performance if component is used a lot
You can overcome that by defining a handler on your class
onBoardClick = (event) => this.props.boardClicked(event, this.props.activeCharacter)
render = () => <div id="board" onClick={this.onBoardClick} />
Now you are passing the same function reference each time so no unnecessary re-renders but same click handler functionality