A useState and Context API pattern that avoids redundant renders
My initial attempt at a basic pattern that combines React Context with useState was as follows:
But I soon noticed this caused renders that I wasn’t expecting (compared to a more traditional Redux and class based components approach):
Creating new objects
On a side note, the above code creates a new object every time the state changes:
const [value, set] = useState("initial");
return <MyContext.Provider value={{ value, set }} {...props} />;
A better approach for this would be as follows:
const valueState = useState("initial");
return <MyContext.Provider value={valueState} {...props} />;
Split contexts that don’t change together
In the previous example, the SetValue component renders because it is using the same context provider as the changing value. But the set
function doesn’t change so there should be no need to render the button.
Option 1 (Preferred): Split contexts that don’t change together
Option 2: Split your component in two, put memo in between
Option 3: One component with useMemo inside
https://github.com/facebook/react/issues/15156#issuecomment-474590693
Dan Abramov recommends splitting contexts that don’t change together. In our simple example, set
and value
don’t change together.
Following this advice results in the following:
This prevents SetValue from rendering when value changes, but there are still renders that aren’t needed:
In this example, the Provider component renders when the state changes, which causes SetContext to render.
We can swap the order of SetContext and ValueContext but still have a similar issue:
Both context providers still render:
Provider renders child
In this basic example, this may be a micro optimisation, but when trying to establish a useState/Context API pattern to use on a larger scale I would prefer to not be rendering the SetContext.Provider if not needed.
This tip results in the following:
And we now have a pattern that results in only rendering what is needed: