Reactjs - How to avoid creating a new clickhandler function in each render - javascript

In my react component on a button click, i am passing a parameter to my click handler exactly like this
<a
id={`_primaryAction_${messageObject.id}`}
href="#"
class='message'
onClick={(e: MouseEvent) =>
this.handleClick(e, messageObject)
}
>
I have a usecase where my props are changing and re render is happening . so in each new render this click handler new instance will create. Is there a way to avoid this ?
Edited: removed id and passing wholeObject as it is my use case. Yes this is in loop . This a tag will create for the array of messages.

First of all, do more research to see if the re-rendering is indeed a cause for concern, as it might not be such a big deal performance-wise.
As a solution, you could create another component which you pass the object.
const ActionLink = (props) => {
const {
handleClick,
messageObject,
...otherProps
} = props;
const clickHandler = React.useCallback((e: MouseEvent) => {
handleClick(e, messageObject);
}, [handleClick, messageObject]);
return <a
{...otherProps}
onClick={ clickHandler }
/>;
}
export default ActionLink;
And in your case, you can use it like the following (instead of the a)
<ActionLink
id={`_primaryAction_${messageObject.id}`}
href="#"
class="message"
messageObject={messageObject}
handleClick={this.handleClick} >...</ActionLink>
And if required, you can further protect against re-renders by passing it through React.memo
export default React.memo(ActionLink);
Lastly as an alternative, you could do as others have suggested and provide the id to an attribute of the link, and use that inside the handleClick method to retrieve the correct message from the list
something like
<a
id={`_primaryAction_${messageObject.id}`}
href="#"
class='message'
data-message-id={messageObject.id}
onClick={this.handleClick}
>
and in your handleClick
handleClick(e){
const messageId = e.target.getAttribute('data-message-id');
// assuming your message list is named messageList
// adjust accordingly
const message = messageList.find(({ id }) => id === messageId);
// ... do what you were already doing with the message here
}

checkout useCallback
useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders
https://reactjs.org/docs/hooks-reference.html#usecallback

I think you are using a class component
since you want to pass an object which I think is coming dynamically and not some constant in component (i.e. object is part of a map) and also don’t want to create a new function on every render I would suggest set your button attribute's value as the value of your object and you can access it e.target.value and bind the method than using the inline callback
and it will not create a new function now here's the working example

I see you're using class component. In that case, just move the handler into a separate function.
class MyComponent extends React.Component {
handleClick = (e) => {
this.deleteRow(id, e)
}
render() {
return <button onClick={this.handleClick}>Delete Row</button>
}
}

Related

React hooks onclick event with multiple params without unnecessary rerender?

I am using react hooks and functional components and was wondering how I can add multiple params to an react onClick event.
I know there are different options to achieve this. In the past I used this style below (from https://reactjs.org/docs/handling-events.html):
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// This syntax ensures `this` is bound within handleClick
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
}
But now I am facing this exact described problem from the official react docs. I am getting to many rerenders because of these arrow functions in the onClick attribute:
The problem with this syntax is that a different callback is created
each time the LoggingButton 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. We generally
recommend binding in the constructor or using the class fields syntax,
to avoid this sort of performance problem.
I have put my function already in a useCallback hook. But if I use this function in a onClick event with an arrow function it will trigger rerenders again.
If I change it to the function reference only it is not triggering rerenders.
So far this is fine.
But: How do I add multiple parameters to this functionreference when using react hooks and functional components.
Will I get by default always the e (event parameter?) as first parameter?
Can somebody explain to me when and how I am getting the react event parameter and when I will not receive this event?
How can I add multiple params beside the event parameter in my onClick attribute?
For example:
What if I have this function and want to use it in the react onClick attribute, prevent unnecessary rerender and add multiple different parameter in the function call
const myClickFunction = (e, value1, value2, value3) => {
// ...
}
// this would trigger rerenders because of the arrow function how do I prevent this?
<button onClick={(e) => myClickFunction(e, "input1", "input2", "input3")}>
Click me
</button>
One trick I like to use in this case is to "bind" the parameters to rendered element using data attributes
const myClickFunction = (e) => {
const value1 = e.currentTarget.getAttribute('data-value1')
const value2 = e.currentTarget.getAttribute('data-value2')
const value2 = e.currentTarget.getAttribute('data-value2')
}
// this would trigger rerenders because of the arrow function how do I prevent this?
<button onClick={myClickFunction} data-value1="a" data-value2="b" data-value3="c">
Click me
</button>
This way you can memoise your function using useCallback safely and you can reuse the same function if you want to pass it to array of children for example. This is not ideal, you couple parents and children and you can only use data which is serializeable to string (basically only primitives).
Better solution would be to store your values somewhere out of component tree so you can access them without closures (for example in redux-thunk you don't need to pass a lot of stuff around, you can just get data you need from store directly by calling getState)

Should I call a function on every render or use arrow functions in a react class component?

I have the following situation
export default class MyComponent extends Component {
myFunc = dynamicKey => {
// do something with the dynamic key
}
render() {
return (
<Foo>
<button onClick={e => this.myFunc(someDynamicKey1)} />
<button onClick={e => this.myFunc(someDynamicKey2)} />
<button onClick={e => this.myFunc(someDynamicKey3)} />
{/* ... */}
</Foo>
)
}
}
Which is a very common case, but It isn't good because on every render it's creating that arrow function.
So as a walkaround, I made a function that returns another function with that key.
export default class MyComponent extends Component {
myFunc = dynamicKey => e => {
// do something with the dynamic key
}
render() {
return (
<Foo>
<button onClick={this.myFunc(someDynamicKey1)} />
<button onClick={this.myFunc(someDynamicKey2)} />
<button onClick={this.myFunc(someDynamicKey3)} />
{/* ... */}
</Foo>
)
}
}
Now I'm not creating a new function on every render but I'm calling a new function on every render.
Now I'm not sure which one to use. Is calling a function on every render a bad practice? Should I use a arrow function?
When using the curried function, you can use its closure on the current scope.
export default class MyComponent extends Component {
state = {
counter: 42
}
myFunc = dynamicKey => e => {
// closure on the specific this.state.counter value at time of render.
}
}
While returning a new function on every render, its closure is on the recent scope
export default class MyComponent extends Component {
state = {
counter: 42
}
myFunc = dynamicKey => {
// closure on this.state.counter value
}
}
Therefore, it depends on what is the use case.
Ask yourself if the function needs a specific value or the recent one.
Note: if on every render the functions re-declared, it becomes a "difference between function and curried one" question, and for React it doesn't matter as both functions bodies will be executed. So only by memoizing the function (don't call the function with it is called with the same parameters), you can get any noticeable differences.
You can cache your event handlers.
class SomeComponent extends React.Component {
// Each instance of SomeComponent has a cache of click handlers
// that are unique to it.
clickHandlers = {};
// Generate and/or return a click handler,
// given a unique identifier.
getClickHandler = (key) => {
// If no click handler exists for this unique identifier, create one.
if (!this.clickHandlers[key])){
this.clickHandlers[key] = () => alert(key);
}
return this.clickHandlers[key];
}
render() {
return (
<ul>
{this.props.list.map(listItem =>
<li key={listItem.text}>
<Button onClick={this.getClickHandler(listItem.text)} />
</li>
)}
</ul>
);
}
}
see the following article
If you use React hooks then:
const Button = props => {
const onClick = React.useMemo(() => {
alert(listItem.text)
}, [listItem.text]);
}
return <button onClick={onClick}>click</button>
}
If your function does not depend on your component (no this contexts), you can define it outside of the component. All instances of your component will use the same function reference, since the function is identical in all cases.
In contrast to the previous example, createAlertBox remains the same reference to the same location in memory during every render. Button therefore never has to re-render.
While Button is likely a small, quick-to-render component, you may see these inline definitions on large, complex, slow-to-render components, and it can really bog down your React application. It is good practice to simply never define these functions inside the render method.
If your function does depend on your component such that you cannot define it outside the component, you can pass a method of your component as the event handler:
In this case, each instance of SomeComponent has a different alert box. The click event listener for Button needs to be unique to SomeComponent. By passing the createAlertBox method, it does not matter if SomeComponent re-renders. It doesn’t even matter if the message prop changes! The address in memory of createAlertBox does not change, meaning Button does not have to re-render, and you save processing time and improve rendering speed of your application.
For dynamic functions
In this case, you have a variable number of buttons, making a variable number of event listeners, each with a unique function that you cannot possibly know what is when creating your SomeComponent. How can you possible solve this conundrum?
Enter memoization, or what may be easier to refer to as simply, caching. For each unique value, create and cache a function; for all future references to that unique value, return the previously cached function.

Pass parameters to an event handler without creating a new reference of the handler everytime the stateless component re-renders

// #flow
import React from 'react';
import Input from 'components/Input';
import logo from 'assets/images/svg/logo.svg';
import styles from './style.module.css';
type TodoMethod = string => void;
type TodoProps = {
todoList: TodoList,
addTodo: TodoMethod,
deleteTodo: TodoMethod,
};
// Return a function to delete a task
function getDeleteTodoMethod(taskName: string, callback: TodoMethod) {
const deleteTodo = () => {
callback(taskName);
};
return deleteTodo;
}
function Todo({ todoList, addTodo, deleteTodo }: TodoProps) {
return (
<div className={styles.container}>
<header className={styles.appHeader}>
<img src={logo} className={styles.appLogo} alt="logo" />
<a
className={styles.appLink}
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
<main>
<Input onEnter={addTodo} />
{todoList.map((item: TodoItem) => (
<div key={item.index}>
<span>{item.name}</span>
<button
type="button"
onClick={getDeleteTodoMethod(item.name, deleteTodo)}
>
X
</button>
</div>
))}
</main>
</div>
);
}
export default Todo;
I have read in a post that functions should not be created inside a stateless component as re-rendering would cause a re-instantiation of the method. Hence, I created a function outside the component.
deleteTodo is an action that I want to call with a parameter whenever the button DELETE TODO is clicked. This could have been easily accomplished in class-based components via composition.
I could've simply created () => deleteTodo(taskName) and passed it as an event handler but with every re-render a new instance would have been created.
If I create a new stateless component called Button, it would lead me to the same problem of:
if I wanna access the prop taskName, I would have to create the function inside a stateless component
if I create a function outside the component, I have to somehow pass the parameter
Hence, I created a function which returns a named function.
// Return a function to delete a task
function getDeleteTodoMethod(taskName: string, callback: TodoMethod) {
const deleteTodo = () => {
callback(taskName);
};
return deleteTodo;
}
But, whenever the component re-renders, the named function would be assigned again as I feel there is no reference to the named function.
Is my interpretation correct? If yes, then how can I tackle this problem? How can pass a parameter to the handler without recreating the reference to avoid performance limitations?
Also, is there a way to test if a new reference of a function has been created?
The performance impact of defining functions inside the render function is negligible, according to the official documentation:
https://reactjs.org/docs/hooks-faq.html#are-hooks-slow-because-of-creating-functions-in-render
If you want to prevent children from re-rendering when the callbacks change, use hooks to create a memoized callback:
https://reactjs.org/docs/hooks-reference.html#usecallback
You should first find out why the re-render happens. You have three property two of them are functions, those ones are not supposed to change ever.
So it probably happens because of the todolist, if the key (item.index) does not change for the existing items, react will know not to re-render them.
So you can do this:
<button
type="button"
onClick={() => deleteTodo(item.name)}
>
X
</button>
There are other solutions like using React.memo, but you should use that only when you're sure it is necessary to prevent a re-render, since using React.memo also expensive, most of the time it is better if you let the re-render happen.

Lifing state up: Communicating from child component and upward

When I try to modify a base component's variable from a child component. I find that I can only do it by strictly doing the following:
1: Base component must have defined an event handler, strictly a variable onVariableChange event handler, and have it assigned to a local function
2: Base component must have custom attribute variable that will be linked with the above onVariableChange function
3: Child component can now call the this.props.onVariableChange() to make the appropriate modification (from child to base)
in Base declaration:
changeFn(){ //do Something }
Base's render:
return <div> <Child variable={this.stateSomeVar} onVariableChange={this.changeFn} />
in Child:
this.props.onVarChange();
Why is that? Why can't we just call the custom function from child to base directly without the use of custom property?
Am I incorrectly understanding the React's documentation?
in Base:
childFnAnsweredByBase(){
...
}
render(){
return <Child callFromChildFn={this.childFnAnsweredByBase} />
}
REF:
https://reactjs.org/docs/lifting-state-up.html
When I try to modify a base component's variable from a child
component. I find that I can only do it by strictly doing the
following:
I think you mean with variable, base or more accurate parent component's state.
1: Base component must have defined an event handler, strictly a
variable onVariableChange event handler, and have it assigned to a
local function
I don't know what do you mean by saying "strictly", but yes in order to do that parent should have a handler method. The name here is not important, just pass this properly to your child component.
2: Base component must have custom attribute variable that will be
linked with the above onVariableChange function
This variable or state property doesn't need to be linked anywhere and you don't have to pass this to your child component. If child component will use it yes you can pass, but in order to change this in the parent component, it is not needed to be passed to the child.
this.props.onVarChange();
Why is that? Why can't we just call the custom function from child to
base directly without the use of custom property?
If you mean saying by "property" the value itself, again, you don't need to pass it to the child. But, if you mean props, then you should use like that since this function is a part of the child's props.
Here is an example of how you do it without passing the "variable":
const Child = (props) => (
<div>
<input onChange={props.callFromChildFn} />
</div>
);
class App extends React.Component {
state = {
someVar: "initial value",
}
childFnAnsweredByBase = event =>
this.setState({ someVar: event.target.value })
render() {
return (
<div>
<Child callFromChildFn={this.childFnAnsweredByBase} />
<p>someVar is: {this.state.someVar}</p>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Am I incorrectly understanding the React's documentation?
Probably yes. Personally, I don't like that part of the documentation. They are trying to explain an edge case there. Two child components are syncing with some parent's state and show this value with an appropriate situation. Like, one of them shows this value as Fahrenheit and the other one shows it as Celcius. This is why they are passing the state variable (after some conversion) to these components.
In the example above we don't use this state variable in our child component, this is why we don't need it. Here is an example (just a simple, stupid example) showing that how can we use it and why the parent is passing it.
const Child = (props) => {
const { someNum, multiplyTheNumberBy, by } = props;
const handleMultiply = () => {
const newNum = someNum * by;
multiplyTheNumberBy( newNum );
}
return (
<div>
<button onClick={handleMultiply}>Multiply Number By {by}</button>
</div>
);
}
class App extends React.Component {
state = {
someNum: 1,
}
multiplyTheNumberBy = valueFromChild =>
this.setState({ someNum: valueFromChild })
render() {
return (
<div>
<Child
multiplyTheNumberBy={this.multiplyTheNumberBy}
someNum={this.state.someNum}
by={10}
/>
<Child
multiplyTheNumberBy={this.multiplyTheNumberBy}
someNum={this.state.someNum}
by={100}
/>
<p>someNum is: {this.state.someNum}</p>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Update after comments
Also, why do we have to assign const localFn = props.CallFromChildFn ?
why can't we just invoke this.props.CallFromChildFn directly? Or is it
supposed to be props.CallFromChildFn?
First things first. We use this in a class component, so for a functional component, it is not necessary. We can use our props as props.something instead of this.props.something.
Now, the second question is about applying the best practice for performance reasons. For a small app this may not be a problem but for larger apps which has multiple children, components may be problematic.
When defining your functions in a JSX prop, if you use an arrow function and invoke them immediately, or bind it to this there to use it properly, this function is recreated in every render. This is why use references to this functions instead of immediately invoke them somehow or use bind.
Examples.
Think about my first example.
<input onChange={props.callFromChildFn} />
Here, I used the reference of my function and it workes. Since I don't invoke any function here it is not recreated every time when my component renders. I would use it in this way:
<input onChange={e => props.callFromChildFn( e )} />
Here, we are using a callback for onChange as an arrow function. It takes an event and passes it to our callFromChildFn function. This works, too. But, since we used an arrow function here, this function is created in every render.
Let's see my second example.
const { someNum, multiplyTheNumberBy, by } = props;
const handleMultiply = () => {
const newNum = someNum * by;
multiplyTheNumberBy( newNum );
}
return (
<div>
<button onClick={handleMultiply}>Multiply Number By {by}</button>
</div>
Again, instead of using directly my function, I define a handler function here and use its reference. With this newly created function, I can do multiplication operation and use my multiplyTheNumber function from my props and pass it the calculated value. But again, I would use something like this:
const { someNum, multiplyTheNumberBy, by } = props;
return (
<div>
<button onClick={() => multiplyTheNumberBy(someNum * by)}>Multiply Number By {by}</button>
</div>
As you can see, without creating a new function we can use an onClick callback function and use our multiplyTheNumberBy from our props and do the multiplication directly there. But, this function also recreated in every render.
Yes, with the reference method we use a little more code and for small applications maybe this is not necessary. But, I like to use in this way.

Choosing the correct way to call function in stateful component

So i am having tough time figuring/understanding the correct way to call method inside a class in javascript for example
consider we have stateful component with various method like
addIngredientHandler = (type) => { //Adds one to the state of ingredient }
and
purchasingHandlerOpen = () => this.setState({purchasing: true}) //will show a order summary pop-up if we have more than one ingredient
We pass both of them to child component (using props) by calling them in a return of our stateful component like this
<BuildControls
ingredientAdded={this.addIngredientHandler}
purchasingHandlerOpen={this.purchasingHandlerOpen}
purchasableHandler={this.state.purchasable} />
and In our stateless child component we do
<BuildControl
ingredientAdded={() => props.ingredientAdded(el.type)}
/>))}
<button className={Classes.OrderButton} disabled={!props.purchasableHandler} onClick={props.purchasingHandlerOpen}>Order</button>
</div
Here we have use this at one place
ingredientAdded={() => props.ingredientAdded(el.type)}
and this in another
onClick={props.purchasingHandlerOpen}>
So my question is when do we call a method/function using {() => props.ingredientAdded(el.type)} and when do we use {props.purchasingHandlerOpen} and when do we probably do something like {props.purchasingHandlerOpen()}
Slight Note: In the above example where i do
<button className={Classes.OrderButton} disabled={!props.purchasableHandler} onClick={props.purchasingHandlerOpen}>Order</button>
If I do something like {props.purchasingHandlerOpen()} it throws infinite render error message, I I do something like {() => props.purchasingHandlerOpen} the button does not work.
First of all, you have to understand that the thing you're passing here are just functions, so there is nothing principally different in those 2 ways
There are few points you need to consider though:
First: since react.js uses shallow comparison, every time you're passing
ingredientAdded={() => props.ingredientAdded(el.type)}
you're actually pass function created just now, so it may cause unneeded calls of your children render function (you could easily avoid this by using shouldComponentUpdate though). This could lead to possible performance issues on big react trees so that you second approach is preferred.
Second: you could easily mix a some value via your first approach, something like
ingredientAdded={() => props.ingredientAdded(el.type, SOMETHING_FROM_STATE)}
Third. You can easily modify your event handlers and pass down them in react tree by generating functions which return functions:
class App extends React.Component {
generateFunction(something) {
return (arg) => {
this.props.myFunction(something, arg)
}
}
render() {
return (
<div>
<FirstComponent onClick={this.generateClickFunction('First')} />
<SecondComponent onClick={this.generateClickFunction('Second')} />
</div>
}
}
}
UPD
onClick should always receive function, not its results, like that:
<button ... onClick={props.purchasingHandlerOpen} />
if you are changing onClick to {props.purchasingHandlerOpen()} you are calling the function, so you're passing its result to props.
If you are changing onClick to {() => purchasingHandlerOpen} you are passing undefined (it's not a props.purchasingHandlerOpen, but purchasingHandlerOpen is undefined) so that React considers there is no a callback passed to the props
{() => props.ingredientAdded(el.type)} creates a new funciton which binds the el.type, see arrow functions
{props.purchasingHandlerOpen} does nothing since we do not execute the function (there are no (), call, apply). We simply pass the function reference.
{props.purchasingHandlerOpen()} runs the function.

Categories

Resources