Performance issue of React Context
?
React Context
and useContext
is often used to avoid prop drilling, however it's known that there's a performance issue. When a context value is changed, all components calling useContext
will re-render.
Example of React Context
: https://codesandbox.io/s/context-selector-demo-ifnqr5?file=/src/ContextOriginal.js
// Result of the console
Render StoreProvider
Render ThemeConsumer
Render ThemeAction
Render LangConsumer
Render LangAction
When the context value is changed, all components re-render => performance issue
Ideally, in this example:
- when changing
theme
=> only StoreProvider
and ThemeConsumer
will re-render
- when changing
lang
=> only StoreProvider
will re-render
What is ContextSelector
?
ContextSelector can solve the problem above => performance increase.
Usage
// use these functions instead of the React `createContext` and `useContext`
import { createContextSelector, useContextSelector } from "@fishbot/context-selector";
Example of ContextSelector
: https://codesandbox.io/s/context-selector-demo-ifnqr5?file=/src/ContextSelector.js
// Result of the console
Render StoreProvider
Render ThemeConsumer
When the context value is changed, only components using that value will re-render.
- when changing
theme
=> only StoreProvider
and ThemeConsumer
will re-render
- when changing
lang
=> only StoreProvider
will re-render
Use case
State management with StoreContext: https://www.npmjs.com/package/@fishbot/store-context
More configs
Selector comparator
Currently, the default comparator used in the useContextSelector
is deepEqual
(see https://gitlab.com/fishbot/libs/context-selector/-/blob/master/src/ContextSelector/utils/deepEqual.ts)
Basically, these are some comparison examples with deepEqual
-
Array comparison
[] and [] => equal
[1,2] and [1,2] => equal
[] and [1] => not equal
[1,2] and [1,3] => not equal
-
Object comparison
{} and {} => equal
{a: 1, b: 2} and {a: 1, b: 2} => equal
{} and {a: 1} => not equal
{a: 1, b: 2} and {a: 1, b: 3} => not equal
Note that, in vanilla JS
[]
and []
are not equal, i.e. [] === []
is false
{}
and {}
are not equal, i.e. {} === {}
is false
[1,2]
and [1,2]
are not equal, i.e. [1,2] === [1,2]
is false
{a: 1, b: 2}
and {a: 1, b: 2}
are not equal, i.e. {a: 1, b: 2} === {a: 1, b: 2}
is false
A custom comparator can be passed via the last parameter of the useContextSelector
.
(see https://gitlab.com/fishbot/libs/context-selector/-/blob/master/src/ContextSelector/useContextSelector.ts)
Example:
const customComparator = (objA, objB) => {
if (objA > objB) return true;
return false;
}
const foo = useContextSelector(contextObj, contextValue => contextValue.foo, customComparator);