<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
Related
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.
The answer is probably, yes. Because it works, and we use it all day, I know. I am asking to be %100 sure and for future learners of React. I couldn't see it in the official docs of React, except giving an example for passing multiple arguments to event handlers alongside the event object. So for example: As you can see onFormSubmit, although not having an event argument inside the JSX reference, it has access to the event object to do stuff (preventing page refresh on this example) at the execution.
If you write the onFormSubmit as an inline arrow function like onChange handler, you need to pass the event object, then it is not automatic.
class SearchBar extends React.Component {
constructor(props) {
super(props)
this.state = { term:''}
this.onFormSubmit = this.onFormSubmit.bind(this)
}
onFormSubmit(event) {
event.preventDefault()
console.log(this.state.term)
}
render() {
return (
<div className="ui segment" >
<form onSubmit={this.onFormSubmit} className="ui form" >
<div className="field" >
<label>Search</label>
<input type="text"
value = {this.state.term}
onChange={event => this.setState({ term: event.target.value })} />
</div>
</form>
</div>
)
}
}
export default SearchBar
You have defined the function to your onChange event handlers which calls the submit method passing the necessary arguments implicity.
There is nothing special about event handlers in React. Every function, if defined works this way.
const a = [1, 2, 3];
function print(elem, idx) {
console.log(elem, idx);
}
// here you have defined the print function.
// the elem and idx are passed implicitly to run the function.
a.forEach(print);
or
// now, rather than defining the function here.
// you have used another arrow function.
// then you need to pass the arguments explicitly.
a.forEach((elem, idx) => print(elem, idx));
React approaches the event handling a little bit differently, using Synthetic Events but this is how callback handlers work generally. If you use a function reference there, the event object is the only argument passed to your function. So, if the event object is the only argument you want to get then you don't need to use an arrow function.
If you want to pass other variables alongside with the event object, then you can use an arrow function.
<form onSubmit={e => this.onFormSubmit(e, otherVar)} className="ui form" >
So, your callback gets the event parameter and you pass this to your handler function with your other variables.
onFormSubmit(event, otherVar) {
event.preventDefault()
console.log(otherVar)
}
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.