Avoid unneccessary re-rendering in React dynamic nested components

2.2k views Asked by At

The state for my project includes a list of nested animals eg:

    {"europe":{"air":{name:"warbler", points:0}}}

My components are generated based on this data, and at the lowest level (the animal itself), there is a button which is currently triggering a series of callbacks to the highest level, starting a dispatch to the reducer. Every time a button is clicked, all the components from every continent re-render. Would it be better to implement useContext, even though every level of component requires some amount of data from the state object? I tried to implement useCallback, to prevent re-rendering but I didn't know which callbacks were causing it. What would be the best way to optimize rendering a series of nested components (without redux)?

Inside App component

 {Object.entries(state.animalData).map(([continent, areas]) => (
                    <Continent
                        key={continent}
                        areas={areas}
                        totals={state.totals.continents[continent]}
                        handleVote={(
                            num: number,
                            animal: string,
                            area: string
                        ) => triggerChange(num, animal, area, continent)}
                    />
                ))}

Inside Continent component

               <Area
                    key={area}
                    area={area}
                    animals={animals}
                    onVote={(num: number, animal: string) =>
                        handleVote(num, animal, area)
                    }
                  />

Inside Area component

    {animals.map(animal => (
                <Animal
                    key={animal.name}
                    animal={animal}
                    voted={(num: number) => onVote(num, animal.name)}
                />
            ))}

Inside Animal component

          <div>
            <h4>{animal.name}</h4>
            <div>
                <button onClick={voted(+1)}> Upvote </button>
                <button onClick={voted(-1)}> Downvote </button>
            </div>
            <h4>{`${animal.points} Points`}</h4>
            <hr />
          </div>
1

There are 1 answers

4
Johannes Klauß On

Your components trigger a re render because you are using inline defined functions everywhere and those aren't referentially stable. You can use the useCallback (https://reactjs.org/docs/hooks-reference.html#usecallback) hook to prevent re renders.

But you could also use a context provider (as you suggested) which holds the voted callback. That way you don't have to use prop drilling to get the function to the component where it is needed.

The basic solution for this is explained in detail here: https://medium.com/@jeromefranco/how-to-avoid-prop-drilling-in-react-7e3a9f3c8674