How to avoid re-rendering in nested lists in React
I have a hierarchical recursive data structure. It is a list of named items, and each item can have its own list of items. It looks like this:
const items = [
{
name: 'item1',
items: [
{name: 'item2'},
{name: 'item3'},
{name: 'item4', items: [
{
name: 'item5'
}
]},
]
},
{
name: 'item6'
}
]
I want to display it in an unordered list with nested signatures. I also need to be able to mark some items as selected and highlight all items. Due to the second requirement, information about the selected items must be in the top-level component.
My naive implementation looks like this:
class App extends Component {
constructor() {
super(...arguments)
this.state = {
items: items,
highlightedItemsNames: {},
}
this.handleHighligtItem = (item) => {
this.setState({highlightedItemsNames: {
...this.state.highlightedItemsNames, [item.name]: true
}})
}
this.handleDehighlightAllItems = () => {
this.setState({highlightedItemsNames: {}})
}
}
render() {
return <div>
<button onClick={this.handleDehighlightAllItems}>dehighlight</button>
<List
items={this.state.items}
highlightedItemsNames={this.state.highlightedItemsNames}
onHighlight={this.handleHighligtItem}
/>
</div>
}
}
function List(props) {
return <ul>
{props.items.map(item => <li key={item.name}>
<Item
item={item}
highlighted={props.highlightedItemsNames[item.name]}
highlightedItemsNames={props.highlightedItemsNames}
onHighlight={props.onHighlight}
/>
</li>)}
</ul>
}
function Item(props) {
let className = "item"
if (props.highlighted) {
className += '-highlighted'
}
console.log('renders item', props.item.name)
return <div>
<span className={className}>{props.item.name}</span>
<button onClick={() => props.onHighlight(props.item)}>highlight</button>
{props.item.items ? <List
items={props.item.items}
highlightedItemsNames={props.highlightedItemsNames}
onHighlight={props.onHighlight}
/> : null}
</div>
}
But now when I highlight the highlightedItemsNames
change item and each one Item
re-renders. To display the data correctly, you only need to redisplay the selected item and its parents. So how can I avoid all unnecessary re-renders?
In my application, the list of items can be a bit large, and re-rendering all causes the application to noticeably freeze the click.
I could implement shouldComponentUpdate
in Item
that checks if any subitem should be redrawn, but does this have a real chance of improving performance over virtual dom?
I am not using Redux or any other government control library, and I would rather not do that for now.
source to share
ShouldComponentUpdate
designed to check and decide whether or not re-processing is necessary. It is, of course, a smart decision to implement in cases where large items are re-processed unnecessarily. I think you should try to implement this event and see the performance. I believe that trying is what makes most things perfect.
source to share