This probably doesn't just apply to React Native, but I do want to understand what's happening here.
Take the following 5 lines of code. Line 3 will cause the app not to render in Expo.
<Button onPress={this.filterOfficeTypePitt} title="Pitt" />
<Button onPress={this.filterOfficeTypeAtl} title="Atl" />
<Button onPress={this.filterOfficeType("pitt")} title="DOES NOT WORK" />
<Button onPress={(e) => this.filterOfficeType("pitt")} title="Pitt" />
<Button onPress={(e) => this.filterOfficeType("atl")} title="Atl" />
I somewhat understand what is going on in each line.
Line 1 and 2: Straight forward call to a method within the component.
Line 3: Trying to do the same thing and pass a value. This causes the application to crash. Reason is maximum depth exceeded.
Line 4 and 5: I think this is a function passing an anon function, and suddenly allowing me to add in a string value.
Once I used 4 and 5, I was able to use a single method and have it filter the list. When I was using 1 and 2, I had to have a unique method for each.
I just want to understand what it going on better and why exactly #3 will not work. I'm sure I need at least a better understanding of arrow functions.
Including the code for the helper function. It basically grabs data from an index array and pushes it into a FlatList component.
filterOfficeType(officetype){
let newarray = [];
this.state.fulldataSource.forEach((element, index) => {
if(element.office === officetype) {
newarray.push(element);
}
});
this.setState({
dataSource:newarray,
office:officetype
});
}
filterOfficeTypePitt = () => {
this.filterOfficeType("pitt");
}
filterOfficeTypeAtl = () => {
this.filterOfficeType("atl");
}
Line 3 is executing the function and trying to assign the result of that to onPress prop. It never gets there(Why? : explained below)
const Button = {
onPress: this.filterOfficeType("pitt")
}
Note: function is being called at the creation of Button object.
whereas the other lines are assigning the function to the onPress prop
const Button = {
onPress: this. filterOfficeTypePitt
}
or
const Button = {
onPress: (e) => {
this.filterOfficeType("pitt")
}
}
Note: the function is not being called a the Button object creation rather when something presses that button
And Why Line 3 causes the application to crash is because, it triggers a state change by calling setState . When setState is called, react will call render() again. But this render() will execute the function again and this will call setState and react will call render() again and so the maximum depth exceeded and crash
The main difference between arrow functions and normal functions is the this scope. In arrow functions this refers to its parent object, where as in normal function this refers to itself. You can read more about arrow functions
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.
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.
I have a simple Parent Functional Component (App.js) and a child Functional component (counters.jsx), I want to pass a signal/prop from child to parent. I want to send the props as an argument to another function in my child component. I can easily trigger parent function by onClick={props.parentCallback}. but if I send the same props as an argument to another function in child component, I get the error "Expected an assignment or function call and instead saw an expression no-unused-expressions"
//This is parent Component, App.js
function App() {
return (
<div className="App">
<Counters parentCallback = {handleCallback}/>
</div>
);
}
const handleCallback = () => {
console.log("CALLED FRM CHILD");
}
//This is Child Component- Counters.jsx
function Counters (props) {
return (
<div>
<button onClick={ ()=> parentHandler(props) }>CALL PARENT</button><hr/>
{//onClick={props.parentCallback} - This works well}
</div>
);
};
function parentHandler (props) {
props.parentCallback; // This line throws the aforementioned error
}
You are just referencing the function. Not calling it. You can modify parentHandler function as
function parentHandler (props) {
props.parentCallback()
}
no-unused-expressions usually means that you evaluated a value but never used. This rule aims to eliminate unused expressions.
For example if(true) 0
In your case you are calling props.parentCallback without parenthesis, leaving unused. Either you add parenthesis after your function call like this props.parentCallback() or return some value at the end. By that your eslint error will disappear.
Also onClick={props.parentCallback} - This works well because you are just passing a reference of your function to the parent Component. But here onClick={ ()=> parentHandler(props) } you are creating a new inline function on each render.
Thank u for ur answer guys, but none of the answer is exactly what I wanted. Finally, I got the answer myself as it is:
Do you need the function to run now?
Then add the () to execute it
Do you need to function to be referenced so it is called later?
Do not add the ().
More example:
onClick=parentCallback() means call "parentCallback() ASAP, and set its return value to onClick".
On the other hand, onClick=parentCallback means "onClick is an alias for parentCallback. If you call onClick(), you get the same results as calling parentCallback()"
I found it by googling "function call and function reference".
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.
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.