I am trying to optimize my React app in order to remove unnecessary renders.
Please refer to this Snack for an example: https://snack.expo.io/bUZpyw0kH
In this example app, there are two state variables stored in context. One of these values is updated every second, the other never changes.
What I am trying to wrap my head around is why console.log('rerender');
is called every second.
From my understanding, when you destructure variables stored in context, you only receive updates when the destructured variables change. I am only accessing otherValue
, and not elapsed
which changes every second. On this basis, why I am seeing console.log('rerender')
?
In this instance, console.log('context child render');
isn’t called every time because I have wrapped this component with React.memo
.
Should I change this or is wrapping ContextChild
in React.memo efficient enough? Do I need to split into multiple providers? And if I do, what if the component needs to access variables from both contexts?
Advertisement
Answer
From my understanding, when you destructure variables stored in context, you only receive updates when the destructured variables change
Unfortunately, React can’t tell whether the value being read from context is being destructured or not. Context provides a single value, and React only knows that a component is consuming the value from a provider. The consumers of a provider will rerender whenever the value changes.
In the example, the single value is made up of 2 state values and a state setter. One of these values changes regularly, causing a state update and then a rerender for the provider. The render creates a new object to set as the value for the provider (value={{ elapsed, otherValue, setOtherValue }}
is a new object). This new object is then checked against the previous value to see if consumers need to update. Because the two values can’t be the same ({} !== {} === true
), an update will happen for each of the consumers.
Should I change this or is wrapping
ContextChild
in React.memo efficient enough?
This is subjective and needs to be thought about for every app individually.
Is optimization needed at all?
Are the updates for the consumers reading only otherValue
expensive? If the updates are cheap, and there aren’t very many of them, no optimization is needed. If the updates are noticeable and cause lag when scrolling or interacting with the page, then optimization is probably needed.
Which optimization should you make?
Do most consumers need both values, or do most only read one of the values? If most components only use one value from the object, it probably makes sense to split the context into two separate providers to cater to the two separate use cases. However, if there are a small number that only care about one of the values compared to the number that need both, the React.memo
solution is probably enough to address those cases without having to bloat the other consumers with code for reading from multiple contexts.