How to avoid bind or inline arrow functions inside render method
We should avoid method binding inside render because during re-rendering it will create the new methods instead of using the old one, that will affect the performance.
So for the scenarios like this:
<input onChange = { this._handleChange.bind(this) } ...../>
We can bind _handleChange method either in constructor:
this._handleChange = this._handleChange.bind(this);
Or we can use property initializer syntax:
_handleChange = () => {....}
Now lets consider the case where we want to pass some extra parameter, lets say in a simple todo app, onclick of item i need to delete the item from array, for that i need to pass either the item index or the todo name in each onClick method:
todos.map(el => <div key={el} onClick={this._deleteTodo.bind(this, el)}> {el} </div>)
For now just assume that todo names are unique.
As per DOC:
The problem with this syntax is that a different callback is created
each time the component renders.
Question:
How to avoid this way of binding inside render method or what are the alternatives of this?
Kindly provide any reference or example, thanks.
javascript reactjs
add a comment |
We should avoid method binding inside render because during re-rendering it will create the new methods instead of using the old one, that will affect the performance.
So for the scenarios like this:
<input onChange = { this._handleChange.bind(this) } ...../>
We can bind _handleChange method either in constructor:
this._handleChange = this._handleChange.bind(this);
Or we can use property initializer syntax:
_handleChange = () => {....}
Now lets consider the case where we want to pass some extra parameter, lets say in a simple todo app, onclick of item i need to delete the item from array, for that i need to pass either the item index or the todo name in each onClick method:
todos.map(el => <div key={el} onClick={this._deleteTodo.bind(this, el)}> {el} </div>)
For now just assume that todo names are unique.
As per DOC:
The problem with this syntax is that a different callback is created
each time the component renders.
Question:
How to avoid this way of binding inside render method or what are the alternatives of this?
Kindly provide any reference or example, thanks.
javascript reactjs
add a comment |
We should avoid method binding inside render because during re-rendering it will create the new methods instead of using the old one, that will affect the performance.
So for the scenarios like this:
<input onChange = { this._handleChange.bind(this) } ...../>
We can bind _handleChange method either in constructor:
this._handleChange = this._handleChange.bind(this);
Or we can use property initializer syntax:
_handleChange = () => {....}
Now lets consider the case where we want to pass some extra parameter, lets say in a simple todo app, onclick of item i need to delete the item from array, for that i need to pass either the item index or the todo name in each onClick method:
todos.map(el => <div key={el} onClick={this._deleteTodo.bind(this, el)}> {el} </div>)
For now just assume that todo names are unique.
As per DOC:
The problem with this syntax is that a different callback is created
each time the component renders.
Question:
How to avoid this way of binding inside render method or what are the alternatives of this?
Kindly provide any reference or example, thanks.
javascript reactjs
We should avoid method binding inside render because during re-rendering it will create the new methods instead of using the old one, that will affect the performance.
So for the scenarios like this:
<input onChange = { this._handleChange.bind(this) } ...../>
We can bind _handleChange method either in constructor:
this._handleChange = this._handleChange.bind(this);
Or we can use property initializer syntax:
_handleChange = () => {....}
Now lets consider the case where we want to pass some extra parameter, lets say in a simple todo app, onclick of item i need to delete the item from array, for that i need to pass either the item index or the todo name in each onClick method:
todos.map(el => <div key={el} onClick={this._deleteTodo.bind(this, el)}> {el} </div>)
For now just assume that todo names are unique.
As per DOC:
The problem with this syntax is that a different callback is created
each time the component renders.
Question:
How to avoid this way of binding inside render method or what are the alternatives of this?
Kindly provide any reference or example, thanks.
javascript reactjs
javascript reactjs
edited Dec 21 '18 at 6:05
Shubham Khatri
90.7k15112152
90.7k15112152
asked Jul 12 '17 at 9:22
Mayank ShuklaMayank Shukla
40.1k76385
40.1k76385
add a comment |
add a comment |
4 Answers
4
active
oldest
votes
First: A simple solution will be to create a component for the content inside a map function and pass the values as props and when you call the function from the child component you can pass the value to the function passed down as props.
Parent
deleteTodo = (val) => {
console.log(val)
}
todos.map(el =>
<MyComponent val={el} onClick={this.deleteTodo}/>
)
MyComponent
class MyComponent extends React.Component {
deleteTodo = () => {
this.props.onClick(this.props.val);
}
render() {
return <div onClick={this.deleteTodo}> {this.props.val} </div>
}
}
Sample snippet
class Parent extends React.Component {
_deleteTodo = (val) => {
console.log(val)
}
render() {
var todos = ['a', 'b', 'c'];
return (
<div>{todos.map(el =>
<MyComponent key={el} val={el} onClick={this._deleteTodo}/>
)}</div>
)
}
}
class MyComponent extends React.Component {
_deleteTodo = () => {
console.log('here'); this.props.onClick(this.props.val);
}
render() {
return <div onClick={this._deleteTodo}> {this.props.val} </div>
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'));<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="app"></div>EDIT:
Second: The other approach to it would be to use memoize and return a function
constructor() {
super();
this._deleteTodoListener = _.memoize(
this._deleteTodo, (element) => {
return element.hashCode();
}
)
}
_deleteTodo = (element) => {
//delete handling here
}
and using it like
todos.map(el => <div key={el} onClick={this._deleteTodoListener(el)}> {el} </div>)
P.S. However this is not a best solution and will still result in
multiple functions being created but is still an improvement over the
initial case.
Third: However a more appropriate solution to this will be to add an attribute to the topmost div and get the value from event like
_deleteTodo = (e) => {
console.log(e.currentTarget.getAttribute('data-value'));
}
todos.map(el => <div key={el} data-value={el} onClick={this._deleteTodo}> {el} </div>)
However, in this case the attributes are converted to string using toString method and hence and object will be converted to [Object Object] and and array like ["1" , "2", "3"] as "1, 2, 3"
yes we can do that, but with this we need to create separate components and put the parent child relation, i think this will be not that scalable since in large application we used to do this kind of binding at multiple places.
– Mayank Shukla
Jul 12 '17 at 9:38
1
I've struggled with this as well, and my conclusion is that if this recreation of functions is slowing your application down (which... I guess... could happen if you have a large enough data set that re-renders a lot), you should follow this approach for those components. Otherwise, it's not really an issue for perf, and therefore can be ignored safely.
– Kris Selbekk
Jul 12 '17 at 9:41
Yes but that how you can avoid what you want to and scalabilty shouldn't be a problem here
– Shubham Khatri
Jul 12 '17 at 9:42
In your first two code blocks when you create the class property, you usedeleteTodo, but when you make reference to it you use_deleteTodo. Is this a mistake or does the underscore have some special purpose here?
– kojow7
Feb 12 '18 at 15:38
@kojow7 Thanks for pointing that out, it was a typo caused due to copying and code from the question and adding my own
– Shubham Khatri
Feb 12 '18 at 15:47
|
show 1 more comment
This answer https://stackoverflow.com/a/45053753/2808062 is definitely exhaustive, but I'd say it's better to solve this problem by implementing a proper shouldComponentUpdate in the child component.
Even if the props are exactly the same, the following code will still re-render children unless they prevent it in their own shouldComponentUpdate (they might inherit it from PureComponent):
handleChildClick = itemId => {}
render() {
return this.props.array.map(itemData => <Child onClick={this.handleChildClick} data={itemData})
}
Proof: https://jsfiddle.net/69z2wepo/92281/.
So, in order to avoid re-renders, the child component has to implement shouldComponentUpdate anyway. Now, the only reasonable implementation is completely ignoring onClick regardless of whether it has changed:
shouldComponentUpdate(nextProps) {
return this.props.array !== nextProps.array;
}
add a comment |
How to avoid this way of binding inside render method or what are the
alternatives of this?
If you care about re-rendering then shouldComponentUpdate and PureComponent are your friends and they will help you optimize rendering.
You have to extract "Child" component from the "Parent" and pass always the same props and implement shouldComponentUpdate or use PureComponent. What we want is a case when we remove a child, other children shouldn't be re-rendered.
Example
import React, { Component, PureComponent } from 'react';
import { render } from 'react-dom';
class Product extends PureComponent {
render() {
const { id, name, onDelete } = this.props;
console.log(`<Product id=${id} /> render()`);
return (
<li>
{id} - {name}
<button onClick={() => onDelete(id)}>Delete</button>
</li>
);
}
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
products: [
{ id: 1, name: 'Foo' },
{ id: 2, name: 'Bar' },
],
};
this.handleDelete = this.handleDelete.bind(this);
}
handleDelete(productId) {
this.setState(prevState => ({
products: prevState.products.filter(product => product.id !== productId),
}));
}
render() {
console.log(`<App /> render()`);
return (
<div>
<h1>Products</h1>
<ul>
{
this.state.products.map(product => (
<Product
key={product.id}
onDelete={this.handleDelete}
{...product}
/>
))
}
</ul>
</div>
);
}
}
render(<App />, document.getElementById('root'));
Demo: https://codesandbox.io/s/99nZGlyZ
Expected behaviour
<App /> render()<Product id=1... render()<Product id=2... render()
When we remove <Product id=2 ... only <App /> is re-rendered.
- render()
To see those messages in demo, open the dev tools console.
The same technique is used and described in article: React is Slow, React is Fast: Optimizing React Apps in Practice by François Zaninotto.
thanks for the suggestion, but i think using unique key will solve this one What we want is a case when we remove a child, other children shouldn't be re-rendered since just i want to render a single div with text. this approach will play a big role when component is large and we want to avoid re-rendering of it.
– Mayank Shukla
Jul 12 '17 at 10:02
Usingkeyproperty doesn't solve this issue, look at: codesandbox.io/s/xVZ7pL6E even if you usekeyproperty, render() of other<Product />are also called. The only change between demo and that link isProduct extends Componentinstead ofPureComponent.
– hinok
Jul 12 '17 at 10:46
add a comment |
Documentation encourages to use data-attributes and access them from within evt.target.dataset:
_deleteTodo = (evt) => {
const elementToDelete = evt.target.dataset.el;
this.setState(prevState => ({
todos: prevState.todos.filter(el => el !== elementToDelete)
}))
}
// and from render:
todos.map(
el => <div key={el} data-el={el} onClick={this._deleteTodo}> {el} </div>
)
Also note that this makes sense only when you have performance issues:
Is it OK to use arrow functions in render methods?
Generally speaking, yes, it is OK, and it is often the easiest way to
pass parameters to callback functions.
If you do have performance issues, by all means, optimize!
1
Since your answer is now in 2018, just to share nowadays you can use "React Hook"
– Isaac
Nov 13 '18 at 9:28
Are you talking aboutuseCallback?
– streletss
Nov 13 '18 at 9:30
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f45053622%2fhow-to-avoid-bind-or-inline-arrow-functions-inside-render-method%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
First: A simple solution will be to create a component for the content inside a map function and pass the values as props and when you call the function from the child component you can pass the value to the function passed down as props.
Parent
deleteTodo = (val) => {
console.log(val)
}
todos.map(el =>
<MyComponent val={el} onClick={this.deleteTodo}/>
)
MyComponent
class MyComponent extends React.Component {
deleteTodo = () => {
this.props.onClick(this.props.val);
}
render() {
return <div onClick={this.deleteTodo}> {this.props.val} </div>
}
}
Sample snippet
class Parent extends React.Component {
_deleteTodo = (val) => {
console.log(val)
}
render() {
var todos = ['a', 'b', 'c'];
return (
<div>{todos.map(el =>
<MyComponent key={el} val={el} onClick={this._deleteTodo}/>
)}</div>
)
}
}
class MyComponent extends React.Component {
_deleteTodo = () => {
console.log('here'); this.props.onClick(this.props.val);
}
render() {
return <div onClick={this._deleteTodo}> {this.props.val} </div>
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'));<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="app"></div>EDIT:
Second: The other approach to it would be to use memoize and return a function
constructor() {
super();
this._deleteTodoListener = _.memoize(
this._deleteTodo, (element) => {
return element.hashCode();
}
)
}
_deleteTodo = (element) => {
//delete handling here
}
and using it like
todos.map(el => <div key={el} onClick={this._deleteTodoListener(el)}> {el} </div>)
P.S. However this is not a best solution and will still result in
multiple functions being created but is still an improvement over the
initial case.
Third: However a more appropriate solution to this will be to add an attribute to the topmost div and get the value from event like
_deleteTodo = (e) => {
console.log(e.currentTarget.getAttribute('data-value'));
}
todos.map(el => <div key={el} data-value={el} onClick={this._deleteTodo}> {el} </div>)
However, in this case the attributes are converted to string using toString method and hence and object will be converted to [Object Object] and and array like ["1" , "2", "3"] as "1, 2, 3"
yes we can do that, but with this we need to create separate components and put the parent child relation, i think this will be not that scalable since in large application we used to do this kind of binding at multiple places.
– Mayank Shukla
Jul 12 '17 at 9:38
1
I've struggled with this as well, and my conclusion is that if this recreation of functions is slowing your application down (which... I guess... could happen if you have a large enough data set that re-renders a lot), you should follow this approach for those components. Otherwise, it's not really an issue for perf, and therefore can be ignored safely.
– Kris Selbekk
Jul 12 '17 at 9:41
Yes but that how you can avoid what you want to and scalabilty shouldn't be a problem here
– Shubham Khatri
Jul 12 '17 at 9:42
In your first two code blocks when you create the class property, you usedeleteTodo, but when you make reference to it you use_deleteTodo. Is this a mistake or does the underscore have some special purpose here?
– kojow7
Feb 12 '18 at 15:38
@kojow7 Thanks for pointing that out, it was a typo caused due to copying and code from the question and adding my own
– Shubham Khatri
Feb 12 '18 at 15:47
|
show 1 more comment
First: A simple solution will be to create a component for the content inside a map function and pass the values as props and when you call the function from the child component you can pass the value to the function passed down as props.
Parent
deleteTodo = (val) => {
console.log(val)
}
todos.map(el =>
<MyComponent val={el} onClick={this.deleteTodo}/>
)
MyComponent
class MyComponent extends React.Component {
deleteTodo = () => {
this.props.onClick(this.props.val);
}
render() {
return <div onClick={this.deleteTodo}> {this.props.val} </div>
}
}
Sample snippet
class Parent extends React.Component {
_deleteTodo = (val) => {
console.log(val)
}
render() {
var todos = ['a', 'b', 'c'];
return (
<div>{todos.map(el =>
<MyComponent key={el} val={el} onClick={this._deleteTodo}/>
)}</div>
)
}
}
class MyComponent extends React.Component {
_deleteTodo = () => {
console.log('here'); this.props.onClick(this.props.val);
}
render() {
return <div onClick={this._deleteTodo}> {this.props.val} </div>
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'));<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="app"></div>EDIT:
Second: The other approach to it would be to use memoize and return a function
constructor() {
super();
this._deleteTodoListener = _.memoize(
this._deleteTodo, (element) => {
return element.hashCode();
}
)
}
_deleteTodo = (element) => {
//delete handling here
}
and using it like
todos.map(el => <div key={el} onClick={this._deleteTodoListener(el)}> {el} </div>)
P.S. However this is not a best solution and will still result in
multiple functions being created but is still an improvement over the
initial case.
Third: However a more appropriate solution to this will be to add an attribute to the topmost div and get the value from event like
_deleteTodo = (e) => {
console.log(e.currentTarget.getAttribute('data-value'));
}
todos.map(el => <div key={el} data-value={el} onClick={this._deleteTodo}> {el} </div>)
However, in this case the attributes are converted to string using toString method and hence and object will be converted to [Object Object] and and array like ["1" , "2", "3"] as "1, 2, 3"
yes we can do that, but with this we need to create separate components and put the parent child relation, i think this will be not that scalable since in large application we used to do this kind of binding at multiple places.
– Mayank Shukla
Jul 12 '17 at 9:38
1
I've struggled with this as well, and my conclusion is that if this recreation of functions is slowing your application down (which... I guess... could happen if you have a large enough data set that re-renders a lot), you should follow this approach for those components. Otherwise, it's not really an issue for perf, and therefore can be ignored safely.
– Kris Selbekk
Jul 12 '17 at 9:41
Yes but that how you can avoid what you want to and scalabilty shouldn't be a problem here
– Shubham Khatri
Jul 12 '17 at 9:42
In your first two code blocks when you create the class property, you usedeleteTodo, but when you make reference to it you use_deleteTodo. Is this a mistake or does the underscore have some special purpose here?
– kojow7
Feb 12 '18 at 15:38
@kojow7 Thanks for pointing that out, it was a typo caused due to copying and code from the question and adding my own
– Shubham Khatri
Feb 12 '18 at 15:47
|
show 1 more comment
First: A simple solution will be to create a component for the content inside a map function and pass the values as props and when you call the function from the child component you can pass the value to the function passed down as props.
Parent
deleteTodo = (val) => {
console.log(val)
}
todos.map(el =>
<MyComponent val={el} onClick={this.deleteTodo}/>
)
MyComponent
class MyComponent extends React.Component {
deleteTodo = () => {
this.props.onClick(this.props.val);
}
render() {
return <div onClick={this.deleteTodo}> {this.props.val} </div>
}
}
Sample snippet
class Parent extends React.Component {
_deleteTodo = (val) => {
console.log(val)
}
render() {
var todos = ['a', 'b', 'c'];
return (
<div>{todos.map(el =>
<MyComponent key={el} val={el} onClick={this._deleteTodo}/>
)}</div>
)
}
}
class MyComponent extends React.Component {
_deleteTodo = () => {
console.log('here'); this.props.onClick(this.props.val);
}
render() {
return <div onClick={this._deleteTodo}> {this.props.val} </div>
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'));<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="app"></div>EDIT:
Second: The other approach to it would be to use memoize and return a function
constructor() {
super();
this._deleteTodoListener = _.memoize(
this._deleteTodo, (element) => {
return element.hashCode();
}
)
}
_deleteTodo = (element) => {
//delete handling here
}
and using it like
todos.map(el => <div key={el} onClick={this._deleteTodoListener(el)}> {el} </div>)
P.S. However this is not a best solution and will still result in
multiple functions being created but is still an improvement over the
initial case.
Third: However a more appropriate solution to this will be to add an attribute to the topmost div and get the value from event like
_deleteTodo = (e) => {
console.log(e.currentTarget.getAttribute('data-value'));
}
todos.map(el => <div key={el} data-value={el} onClick={this._deleteTodo}> {el} </div>)
However, in this case the attributes are converted to string using toString method and hence and object will be converted to [Object Object] and and array like ["1" , "2", "3"] as "1, 2, 3"
First: A simple solution will be to create a component for the content inside a map function and pass the values as props and when you call the function from the child component you can pass the value to the function passed down as props.
Parent
deleteTodo = (val) => {
console.log(val)
}
todos.map(el =>
<MyComponent val={el} onClick={this.deleteTodo}/>
)
MyComponent
class MyComponent extends React.Component {
deleteTodo = () => {
this.props.onClick(this.props.val);
}
render() {
return <div onClick={this.deleteTodo}> {this.props.val} </div>
}
}
Sample snippet
class Parent extends React.Component {
_deleteTodo = (val) => {
console.log(val)
}
render() {
var todos = ['a', 'b', 'c'];
return (
<div>{todos.map(el =>
<MyComponent key={el} val={el} onClick={this._deleteTodo}/>
)}</div>
)
}
}
class MyComponent extends React.Component {
_deleteTodo = () => {
console.log('here'); this.props.onClick(this.props.val);
}
render() {
return <div onClick={this._deleteTodo}> {this.props.val} </div>
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'));<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="app"></div>EDIT:
Second: The other approach to it would be to use memoize and return a function
constructor() {
super();
this._deleteTodoListener = _.memoize(
this._deleteTodo, (element) => {
return element.hashCode();
}
)
}
_deleteTodo = (element) => {
//delete handling here
}
and using it like
todos.map(el => <div key={el} onClick={this._deleteTodoListener(el)}> {el} </div>)
P.S. However this is not a best solution and will still result in
multiple functions being created but is still an improvement over the
initial case.
Third: However a more appropriate solution to this will be to add an attribute to the topmost div and get the value from event like
_deleteTodo = (e) => {
console.log(e.currentTarget.getAttribute('data-value'));
}
todos.map(el => <div key={el} data-value={el} onClick={this._deleteTodo}> {el} </div>)
However, in this case the attributes are converted to string using toString method and hence and object will be converted to [Object Object] and and array like ["1" , "2", "3"] as "1, 2, 3"
class Parent extends React.Component {
_deleteTodo = (val) => {
console.log(val)
}
render() {
var todos = ['a', 'b', 'c'];
return (
<div>{todos.map(el =>
<MyComponent key={el} val={el} onClick={this._deleteTodo}/>
)}</div>
)
}
}
class MyComponent extends React.Component {
_deleteTodo = () => {
console.log('here'); this.props.onClick(this.props.val);
}
render() {
return <div onClick={this._deleteTodo}> {this.props.val} </div>
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'));<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="app"></div>class Parent extends React.Component {
_deleteTodo = (val) => {
console.log(val)
}
render() {
var todos = ['a', 'b', 'c'];
return (
<div>{todos.map(el =>
<MyComponent key={el} val={el} onClick={this._deleteTodo}/>
)}</div>
)
}
}
class MyComponent extends React.Component {
_deleteTodo = () => {
console.log('here'); this.props.onClick(this.props.val);
}
render() {
return <div onClick={this._deleteTodo}> {this.props.val} </div>
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'));<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="app"></div>edited Dec 21 '18 at 8:37
answered Jul 12 '17 at 9:28
Shubham KhatriShubham Khatri
90.7k15112152
90.7k15112152
yes we can do that, but with this we need to create separate components and put the parent child relation, i think this will be not that scalable since in large application we used to do this kind of binding at multiple places.
– Mayank Shukla
Jul 12 '17 at 9:38
1
I've struggled with this as well, and my conclusion is that if this recreation of functions is slowing your application down (which... I guess... could happen if you have a large enough data set that re-renders a lot), you should follow this approach for those components. Otherwise, it's not really an issue for perf, and therefore can be ignored safely.
– Kris Selbekk
Jul 12 '17 at 9:41
Yes but that how you can avoid what you want to and scalabilty shouldn't be a problem here
– Shubham Khatri
Jul 12 '17 at 9:42
In your first two code blocks when you create the class property, you usedeleteTodo, but when you make reference to it you use_deleteTodo. Is this a mistake or does the underscore have some special purpose here?
– kojow7
Feb 12 '18 at 15:38
@kojow7 Thanks for pointing that out, it was a typo caused due to copying and code from the question and adding my own
– Shubham Khatri
Feb 12 '18 at 15:47
|
show 1 more comment
yes we can do that, but with this we need to create separate components and put the parent child relation, i think this will be not that scalable since in large application we used to do this kind of binding at multiple places.
– Mayank Shukla
Jul 12 '17 at 9:38
1
I've struggled with this as well, and my conclusion is that if this recreation of functions is slowing your application down (which... I guess... could happen if you have a large enough data set that re-renders a lot), you should follow this approach for those components. Otherwise, it's not really an issue for perf, and therefore can be ignored safely.
– Kris Selbekk
Jul 12 '17 at 9:41
Yes but that how you can avoid what you want to and scalabilty shouldn't be a problem here
– Shubham Khatri
Jul 12 '17 at 9:42
In your first two code blocks when you create the class property, you usedeleteTodo, but when you make reference to it you use_deleteTodo. Is this a mistake or does the underscore have some special purpose here?
– kojow7
Feb 12 '18 at 15:38
@kojow7 Thanks for pointing that out, it was a typo caused due to copying and code from the question and adding my own
– Shubham Khatri
Feb 12 '18 at 15:47
yes we can do that, but with this we need to create separate components and put the parent child relation, i think this will be not that scalable since in large application we used to do this kind of binding at multiple places.
– Mayank Shukla
Jul 12 '17 at 9:38
yes we can do that, but with this we need to create separate components and put the parent child relation, i think this will be not that scalable since in large application we used to do this kind of binding at multiple places.
– Mayank Shukla
Jul 12 '17 at 9:38
1
1
I've struggled with this as well, and my conclusion is that if this recreation of functions is slowing your application down (which... I guess... could happen if you have a large enough data set that re-renders a lot), you should follow this approach for those components. Otherwise, it's not really an issue for perf, and therefore can be ignored safely.
– Kris Selbekk
Jul 12 '17 at 9:41
I've struggled with this as well, and my conclusion is that if this recreation of functions is slowing your application down (which... I guess... could happen if you have a large enough data set that re-renders a lot), you should follow this approach for those components. Otherwise, it's not really an issue for perf, and therefore can be ignored safely.
– Kris Selbekk
Jul 12 '17 at 9:41
Yes but that how you can avoid what you want to and scalabilty shouldn't be a problem here
– Shubham Khatri
Jul 12 '17 at 9:42
Yes but that how you can avoid what you want to and scalabilty shouldn't be a problem here
– Shubham Khatri
Jul 12 '17 at 9:42
In your first two code blocks when you create the class property, you use
deleteTodo, but when you make reference to it you use _deleteTodo. Is this a mistake or does the underscore have some special purpose here?– kojow7
Feb 12 '18 at 15:38
In your first two code blocks when you create the class property, you use
deleteTodo, but when you make reference to it you use _deleteTodo. Is this a mistake or does the underscore have some special purpose here?– kojow7
Feb 12 '18 at 15:38
@kojow7 Thanks for pointing that out, it was a typo caused due to copying and code from the question and adding my own
– Shubham Khatri
Feb 12 '18 at 15:47
@kojow7 Thanks for pointing that out, it was a typo caused due to copying and code from the question and adding my own
– Shubham Khatri
Feb 12 '18 at 15:47
|
show 1 more comment
This answer https://stackoverflow.com/a/45053753/2808062 is definitely exhaustive, but I'd say it's better to solve this problem by implementing a proper shouldComponentUpdate in the child component.
Even if the props are exactly the same, the following code will still re-render children unless they prevent it in their own shouldComponentUpdate (they might inherit it from PureComponent):
handleChildClick = itemId => {}
render() {
return this.props.array.map(itemData => <Child onClick={this.handleChildClick} data={itemData})
}
Proof: https://jsfiddle.net/69z2wepo/92281/.
So, in order to avoid re-renders, the child component has to implement shouldComponentUpdate anyway. Now, the only reasonable implementation is completely ignoring onClick regardless of whether it has changed:
shouldComponentUpdate(nextProps) {
return this.props.array !== nextProps.array;
}
add a comment |
This answer https://stackoverflow.com/a/45053753/2808062 is definitely exhaustive, but I'd say it's better to solve this problem by implementing a proper shouldComponentUpdate in the child component.
Even if the props are exactly the same, the following code will still re-render children unless they prevent it in their own shouldComponentUpdate (they might inherit it from PureComponent):
handleChildClick = itemId => {}
render() {
return this.props.array.map(itemData => <Child onClick={this.handleChildClick} data={itemData})
}
Proof: https://jsfiddle.net/69z2wepo/92281/.
So, in order to avoid re-renders, the child component has to implement shouldComponentUpdate anyway. Now, the only reasonable implementation is completely ignoring onClick regardless of whether it has changed:
shouldComponentUpdate(nextProps) {
return this.props.array !== nextProps.array;
}
add a comment |
This answer https://stackoverflow.com/a/45053753/2808062 is definitely exhaustive, but I'd say it's better to solve this problem by implementing a proper shouldComponentUpdate in the child component.
Even if the props are exactly the same, the following code will still re-render children unless they prevent it in their own shouldComponentUpdate (they might inherit it from PureComponent):
handleChildClick = itemId => {}
render() {
return this.props.array.map(itemData => <Child onClick={this.handleChildClick} data={itemData})
}
Proof: https://jsfiddle.net/69z2wepo/92281/.
So, in order to avoid re-renders, the child component has to implement shouldComponentUpdate anyway. Now, the only reasonable implementation is completely ignoring onClick regardless of whether it has changed:
shouldComponentUpdate(nextProps) {
return this.props.array !== nextProps.array;
}
This answer https://stackoverflow.com/a/45053753/2808062 is definitely exhaustive, but I'd say it's better to solve this problem by implementing a proper shouldComponentUpdate in the child component.
Even if the props are exactly the same, the following code will still re-render children unless they prevent it in their own shouldComponentUpdate (they might inherit it from PureComponent):
handleChildClick = itemId => {}
render() {
return this.props.array.map(itemData => <Child onClick={this.handleChildClick} data={itemData})
}
Proof: https://jsfiddle.net/69z2wepo/92281/.
So, in order to avoid re-renders, the child component has to implement shouldComponentUpdate anyway. Now, the only reasonable implementation is completely ignoring onClick regardless of whether it has changed:
shouldComponentUpdate(nextProps) {
return this.props.array !== nextProps.array;
}
edited Jun 11 '18 at 11:15
answered Nov 22 '17 at 14:59
grebenyuksvgrebenyuksv
94177
94177
add a comment |
add a comment |
How to avoid this way of binding inside render method or what are the
alternatives of this?
If you care about re-rendering then shouldComponentUpdate and PureComponent are your friends and they will help you optimize rendering.
You have to extract "Child" component from the "Parent" and pass always the same props and implement shouldComponentUpdate or use PureComponent. What we want is a case when we remove a child, other children shouldn't be re-rendered.
Example
import React, { Component, PureComponent } from 'react';
import { render } from 'react-dom';
class Product extends PureComponent {
render() {
const { id, name, onDelete } = this.props;
console.log(`<Product id=${id} /> render()`);
return (
<li>
{id} - {name}
<button onClick={() => onDelete(id)}>Delete</button>
</li>
);
}
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
products: [
{ id: 1, name: 'Foo' },
{ id: 2, name: 'Bar' },
],
};
this.handleDelete = this.handleDelete.bind(this);
}
handleDelete(productId) {
this.setState(prevState => ({
products: prevState.products.filter(product => product.id !== productId),
}));
}
render() {
console.log(`<App /> render()`);
return (
<div>
<h1>Products</h1>
<ul>
{
this.state.products.map(product => (
<Product
key={product.id}
onDelete={this.handleDelete}
{...product}
/>
))
}
</ul>
</div>
);
}
}
render(<App />, document.getElementById('root'));
Demo: https://codesandbox.io/s/99nZGlyZ
Expected behaviour
<App /> render()<Product id=1... render()<Product id=2... render()
When we remove <Product id=2 ... only <App /> is re-rendered.
- render()
To see those messages in demo, open the dev tools console.
The same technique is used and described in article: React is Slow, React is Fast: Optimizing React Apps in Practice by François Zaninotto.
thanks for the suggestion, but i think using unique key will solve this one What we want is a case when we remove a child, other children shouldn't be re-rendered since just i want to render a single div with text. this approach will play a big role when component is large and we want to avoid re-rendering of it.
– Mayank Shukla
Jul 12 '17 at 10:02
Usingkeyproperty doesn't solve this issue, look at: codesandbox.io/s/xVZ7pL6E even if you usekeyproperty, render() of other<Product />are also called. The only change between demo and that link isProduct extends Componentinstead ofPureComponent.
– hinok
Jul 12 '17 at 10:46
add a comment |
How to avoid this way of binding inside render method or what are the
alternatives of this?
If you care about re-rendering then shouldComponentUpdate and PureComponent are your friends and they will help you optimize rendering.
You have to extract "Child" component from the "Parent" and pass always the same props and implement shouldComponentUpdate or use PureComponent. What we want is a case when we remove a child, other children shouldn't be re-rendered.
Example
import React, { Component, PureComponent } from 'react';
import { render } from 'react-dom';
class Product extends PureComponent {
render() {
const { id, name, onDelete } = this.props;
console.log(`<Product id=${id} /> render()`);
return (
<li>
{id} - {name}
<button onClick={() => onDelete(id)}>Delete</button>
</li>
);
}
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
products: [
{ id: 1, name: 'Foo' },
{ id: 2, name: 'Bar' },
],
};
this.handleDelete = this.handleDelete.bind(this);
}
handleDelete(productId) {
this.setState(prevState => ({
products: prevState.products.filter(product => product.id !== productId),
}));
}
render() {
console.log(`<App /> render()`);
return (
<div>
<h1>Products</h1>
<ul>
{
this.state.products.map(product => (
<Product
key={product.id}
onDelete={this.handleDelete}
{...product}
/>
))
}
</ul>
</div>
);
}
}
render(<App />, document.getElementById('root'));
Demo: https://codesandbox.io/s/99nZGlyZ
Expected behaviour
<App /> render()<Product id=1... render()<Product id=2... render()
When we remove <Product id=2 ... only <App /> is re-rendered.
- render()
To see those messages in demo, open the dev tools console.
The same technique is used and described in article: React is Slow, React is Fast: Optimizing React Apps in Practice by François Zaninotto.
thanks for the suggestion, but i think using unique key will solve this one What we want is a case when we remove a child, other children shouldn't be re-rendered since just i want to render a single div with text. this approach will play a big role when component is large and we want to avoid re-rendering of it.
– Mayank Shukla
Jul 12 '17 at 10:02
Usingkeyproperty doesn't solve this issue, look at: codesandbox.io/s/xVZ7pL6E even if you usekeyproperty, render() of other<Product />are also called. The only change between demo and that link isProduct extends Componentinstead ofPureComponent.
– hinok
Jul 12 '17 at 10:46
add a comment |
How to avoid this way of binding inside render method or what are the
alternatives of this?
If you care about re-rendering then shouldComponentUpdate and PureComponent are your friends and they will help you optimize rendering.
You have to extract "Child" component from the "Parent" and pass always the same props and implement shouldComponentUpdate or use PureComponent. What we want is a case when we remove a child, other children shouldn't be re-rendered.
Example
import React, { Component, PureComponent } from 'react';
import { render } from 'react-dom';
class Product extends PureComponent {
render() {
const { id, name, onDelete } = this.props;
console.log(`<Product id=${id} /> render()`);
return (
<li>
{id} - {name}
<button onClick={() => onDelete(id)}>Delete</button>
</li>
);
}
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
products: [
{ id: 1, name: 'Foo' },
{ id: 2, name: 'Bar' },
],
};
this.handleDelete = this.handleDelete.bind(this);
}
handleDelete(productId) {
this.setState(prevState => ({
products: prevState.products.filter(product => product.id !== productId),
}));
}
render() {
console.log(`<App /> render()`);
return (
<div>
<h1>Products</h1>
<ul>
{
this.state.products.map(product => (
<Product
key={product.id}
onDelete={this.handleDelete}
{...product}
/>
))
}
</ul>
</div>
);
}
}
render(<App />, document.getElementById('root'));
Demo: https://codesandbox.io/s/99nZGlyZ
Expected behaviour
<App /> render()<Product id=1... render()<Product id=2... render()
When we remove <Product id=2 ... only <App /> is re-rendered.
- render()
To see those messages in demo, open the dev tools console.
The same technique is used and described in article: React is Slow, React is Fast: Optimizing React Apps in Practice by François Zaninotto.
How to avoid this way of binding inside render method or what are the
alternatives of this?
If you care about re-rendering then shouldComponentUpdate and PureComponent are your friends and they will help you optimize rendering.
You have to extract "Child" component from the "Parent" and pass always the same props and implement shouldComponentUpdate or use PureComponent. What we want is a case when we remove a child, other children shouldn't be re-rendered.
Example
import React, { Component, PureComponent } from 'react';
import { render } from 'react-dom';
class Product extends PureComponent {
render() {
const { id, name, onDelete } = this.props;
console.log(`<Product id=${id} /> render()`);
return (
<li>
{id} - {name}
<button onClick={() => onDelete(id)}>Delete</button>
</li>
);
}
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
products: [
{ id: 1, name: 'Foo' },
{ id: 2, name: 'Bar' },
],
};
this.handleDelete = this.handleDelete.bind(this);
}
handleDelete(productId) {
this.setState(prevState => ({
products: prevState.products.filter(product => product.id !== productId),
}));
}
render() {
console.log(`<App /> render()`);
return (
<div>
<h1>Products</h1>
<ul>
{
this.state.products.map(product => (
<Product
key={product.id}
onDelete={this.handleDelete}
{...product}
/>
))
}
</ul>
</div>
);
}
}
render(<App />, document.getElementById('root'));
Demo: https://codesandbox.io/s/99nZGlyZ
Expected behaviour
<App /> render()<Product id=1... render()<Product id=2... render()
When we remove <Product id=2 ... only <App /> is re-rendered.
- render()
To see those messages in demo, open the dev tools console.
The same technique is used and described in article: React is Slow, React is Fast: Optimizing React Apps in Practice by François Zaninotto.
answered Jul 12 '17 at 9:52
hinokhinok
2,4791324
2,4791324
thanks for the suggestion, but i think using unique key will solve this one What we want is a case when we remove a child, other children shouldn't be re-rendered since just i want to render a single div with text. this approach will play a big role when component is large and we want to avoid re-rendering of it.
– Mayank Shukla
Jul 12 '17 at 10:02
Usingkeyproperty doesn't solve this issue, look at: codesandbox.io/s/xVZ7pL6E even if you usekeyproperty, render() of other<Product />are also called. The only change between demo and that link isProduct extends Componentinstead ofPureComponent.
– hinok
Jul 12 '17 at 10:46
add a comment |
thanks for the suggestion, but i think using unique key will solve this one What we want is a case when we remove a child, other children shouldn't be re-rendered since just i want to render a single div with text. this approach will play a big role when component is large and we want to avoid re-rendering of it.
– Mayank Shukla
Jul 12 '17 at 10:02
Usingkeyproperty doesn't solve this issue, look at: codesandbox.io/s/xVZ7pL6E even if you usekeyproperty, render() of other<Product />are also called. The only change between demo and that link isProduct extends Componentinstead ofPureComponent.
– hinok
Jul 12 '17 at 10:46
thanks for the suggestion, but i think using unique key will solve this one What we want is a case when we remove a child, other children shouldn't be re-rendered since just i want to render a single div with text. this approach will play a big role when component is large and we want to avoid re-rendering of it.
– Mayank Shukla
Jul 12 '17 at 10:02
thanks for the suggestion, but i think using unique key will solve this one What we want is a case when we remove a child, other children shouldn't be re-rendered since just i want to render a single div with text. this approach will play a big role when component is large and we want to avoid re-rendering of it.
– Mayank Shukla
Jul 12 '17 at 10:02
Using
key property doesn't solve this issue, look at: codesandbox.io/s/xVZ7pL6E even if you use key property, render() of other <Product /> are also called. The only change between demo and that link is Product extends Component instead of PureComponent.– hinok
Jul 12 '17 at 10:46
Using
key property doesn't solve this issue, look at: codesandbox.io/s/xVZ7pL6E even if you use key property, render() of other <Product /> are also called. The only change between demo and that link is Product extends Component instead of PureComponent.– hinok
Jul 12 '17 at 10:46
add a comment |
Documentation encourages to use data-attributes and access them from within evt.target.dataset:
_deleteTodo = (evt) => {
const elementToDelete = evt.target.dataset.el;
this.setState(prevState => ({
todos: prevState.todos.filter(el => el !== elementToDelete)
}))
}
// and from render:
todos.map(
el => <div key={el} data-el={el} onClick={this._deleteTodo}> {el} </div>
)
Also note that this makes sense only when you have performance issues:
Is it OK to use arrow functions in render methods?
Generally speaking, yes, it is OK, and it is often the easiest way to
pass parameters to callback functions.
If you do have performance issues, by all means, optimize!
1
Since your answer is now in 2018, just to share nowadays you can use "React Hook"
– Isaac
Nov 13 '18 at 9:28
Are you talking aboutuseCallback?
– streletss
Nov 13 '18 at 9:30
add a comment |
Documentation encourages to use data-attributes and access them from within evt.target.dataset:
_deleteTodo = (evt) => {
const elementToDelete = evt.target.dataset.el;
this.setState(prevState => ({
todos: prevState.todos.filter(el => el !== elementToDelete)
}))
}
// and from render:
todos.map(
el => <div key={el} data-el={el} onClick={this._deleteTodo}> {el} </div>
)
Also note that this makes sense only when you have performance issues:
Is it OK to use arrow functions in render methods?
Generally speaking, yes, it is OK, and it is often the easiest way to
pass parameters to callback functions.
If you do have performance issues, by all means, optimize!
1
Since your answer is now in 2018, just to share nowadays you can use "React Hook"
– Isaac
Nov 13 '18 at 9:28
Are you talking aboutuseCallback?
– streletss
Nov 13 '18 at 9:30
add a comment |
Documentation encourages to use data-attributes and access them from within evt.target.dataset:
_deleteTodo = (evt) => {
const elementToDelete = evt.target.dataset.el;
this.setState(prevState => ({
todos: prevState.todos.filter(el => el !== elementToDelete)
}))
}
// and from render:
todos.map(
el => <div key={el} data-el={el} onClick={this._deleteTodo}> {el} </div>
)
Also note that this makes sense only when you have performance issues:
Is it OK to use arrow functions in render methods?
Generally speaking, yes, it is OK, and it is often the easiest way to
pass parameters to callback functions.
If you do have performance issues, by all means, optimize!
Documentation encourages to use data-attributes and access them from within evt.target.dataset:
_deleteTodo = (evt) => {
const elementToDelete = evt.target.dataset.el;
this.setState(prevState => ({
todos: prevState.todos.filter(el => el !== elementToDelete)
}))
}
// and from render:
todos.map(
el => <div key={el} data-el={el} onClick={this._deleteTodo}> {el} </div>
)
Also note that this makes sense only when you have performance issues:
Is it OK to use arrow functions in render methods?
Generally speaking, yes, it is OK, and it is often the easiest way to
pass parameters to callback functions.
If you do have performance issues, by all means, optimize!
answered Nov 13 '18 at 9:24
streletssstreletss
2,408522
2,408522
1
Since your answer is now in 2018, just to share nowadays you can use "React Hook"
– Isaac
Nov 13 '18 at 9:28
Are you talking aboutuseCallback?
– streletss
Nov 13 '18 at 9:30
add a comment |
1
Since your answer is now in 2018, just to share nowadays you can use "React Hook"
– Isaac
Nov 13 '18 at 9:28
Are you talking aboutuseCallback?
– streletss
Nov 13 '18 at 9:30
1
1
Since your answer is now in 2018, just to share nowadays you can use "React Hook"
– Isaac
Nov 13 '18 at 9:28
Since your answer is now in 2018, just to share nowadays you can use "React Hook"
– Isaac
Nov 13 '18 at 9:28
Are you talking about
useCallback?– streletss
Nov 13 '18 at 9:30
Are you talking about
useCallback?– streletss
Nov 13 '18 at 9:30
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f45053622%2fhow-to-avoid-bind-or-inline-arrow-functions-inside-render-method%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown