I'm working on simple stream app. I have list of posts and this list can receive updates, which will display on top of it.
The problem is on each new post receive React rerenders the whole list of elements. I've made simple example for it.
Is there any way to avoid this behaviour? I've seen the dynamic-children topic on React docs, but in example, as you see, I have all children updated anyway.
class Post extends React.Component {
render() {
console.log('rerendered post', this.props.reactKey);
return (
<li>{this.props.post.text}</li>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {posts: [
{id: '00001', text: 'First one'},
{id: '00002',text: 'Second one'},
{id: '00003',text: 'Third one'}
]};
}
addPost() {
const posts = this.state.posts;
posts.unshift({id: '00004', text: 'New post'});
this.setState({posts: posts});
}
render() {
return (
<div>
<button onClick={this.addPost.bind(this)}>Add Post</button>
<ul>
{this.state.posts.map((post, index) => {
return (<Post post={post} key={post.id} reactKey={index} />);
})}
</ul>
</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>
<body>
<div id="root"></div>
</body>
The solution
The problem was that I used index of .map function is a key for each list component instead of unique key. And because after adding new element to list all indexes changes to +1, so the first post becomes the second, all my posts have re-rendered. So at first, check you use unique keys across all list elements :-)
1. Memoization using useMemo() and UseCallback() Hooks. Memoization enables your code to re-render components only if there's a change in the props. With this technique, developers can avoid unnecessary renderings and reduce the computational load in applications.
In ReactJS, changing items in the list when an item of the list is clicked can be done by triggering the event onClick() on the item which is currently clicked.
In order for props to change, they need to be updated by the parent component. This means the parent would have to re-render, which will trigger re-render of the child component regardless of its props.
To get rid of your infinite loop, simply use an empty dependency array like so: const [count, setCount] = useState(0); //only update the value of 'count' when component is first mounted useEffect(() => { setCount((count) => count + 1); }, []); This will tell React to run useEffect on the first render.
Work that needs to be done only once should be done in a lifecycle method that is guaranteed to run only once, like componentDidMount
. As the docs suggest:
If you want to integrate with other JavaScript frameworks, set timers using setTimeout or setInterval, or send AJAX requests, perform those operations in this method.
I added logging to componentDidMount
in your snippet to show rendering happens many times, but componentDidMount
is called only once per instance.
class Post extends React.Component {
componentDidMount() {
console.log('mounted post', this.props.id);
}
render() {
console.log('rerendered post', this.props.id);
return (
<li>{this.props.post.text}</li>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.nextId = 4;
this.state = {
posts: [
{id: 1, text: 'First one'},
{id: 2,text: 'Second one'},
{id: 3,text: 'Third one'},
],
};
}
addPost() {
const posts = this.state.posts;
posts.unshift({id: this.nextId, text: 'Post ' + this.nextId});
this.nextId++;
this.setState({posts: posts});
}
render() {
return (
<div>
<button onClick={this.addPost.bind(this)}>Add Post</button>
<ul>
{this.state.posts.map((post, index) => {
return (<Post post={post} key={post.id} id={post.id} />);
})}
</ul>
</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>
<body>
<div id="root"></div>
</body>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With