use-context-selector v1.4.4
use-context-selector
React useContext with selector support in userland
Introduction
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 that useContext will re-render.
To solve this issue, useContextSelector is proposed and later proposed Speculative Mode with context selector support. This library provides the API in userland.
v1 uses calculateChangedBits=()=>0
technique to stop propagation,
while v2 uses useMutableSource
.
Install
npm install use-context-selector
Usage
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import { createContext, useContextSelector } from 'use-context-selector';
const context = createContext(null);
const Counter1 = () => {
const count1 = useContextSelector(context, v => v[0].count1);
const setState = useContextSelector(context, v => v[1]);
const increment = () => setState(s => ({
...s,
count1: s.count1 + 1,
}));
return (
<div>
<span>Count1: {count1}</span>
<button type="button" onClick={increment}>+1</button>
{Math.random()}
</div>
);
};
const Counter2 = () => {
const count2 = useContextSelector(context, v => v[0].count2);
const setState = useContextSelector(context, v => v[1]);
const increment = () => setState(s => ({
...s,
count2: s.count2 + 1,
}));
return (
<div>
<span>Count2: {count2}</span>
<button type="button" onClick={increment}>+1</button>
{Math.random()}
</div>
);
};
const StateProvider = ({ children }) => {
const [state, setState] = useState({ count1: 0, count2: 0 });
return (
<context.Provider value={[state, setState]}>
{children}
</context.Provider>
);
};
const App = () => (
<StateProvider>
<Counter1 />
<Counter2 />
</StateProvider>
);
ReactDOM.render(<App />, document.getElementById('app'));
Migrating from v1 to v2
In v1:
useContextSelector(context, state => state.count);
In v2:
useContext(context, useCallback(state => state.count, []));
In this case, you can (should) also define the selector function outside render.
API
createContext
This creates a special context for selector-enabled useContext
.
It doesn't pass its value but a ref of the value. Unlike the original context provider, this context provider expects the context value to be immutable and stable.
Parameters
defaultValue
Value
Examples
import { createContext } from 'use-context-selector';
const PersonContext = createContext({ firstName: '', familyName: '' });
useContext
This hook returns context value with optional selector.
It will only accept context created by createContext
.
It will trigger re-render if only the selected value is referentially changed.
The selector must be stable.
Either define selector outside render or wrap with useCallback
.
The selector should return referentially equal result for same input for better performance.
Parameters
context
Context<Value>selector
function (value: Value): Selected (optional, defaultidentity as(value:Value)=>Selected
)
Examples
import { useContext } from 'use-context-selector';
const firstName = useContext(PersonContext, state => state.firstName);
Limitations
- In order to stop propagation,
children
of a context provider has to be either created outside of the provider or memoized withReact.memo
. - Provider trigger re-renders only if the context value is referentially changed.
- Neither context consumers or class components are supported.
- The stale props issue isn't solved.
- Due to the current useMutableSource limitation, a selector can't return a function. (See
02_basic_spec
wrapping setState) - Tearing is only avoided within the Provider tree. A value outside the Provider will tear. (
02_tearing_spec
fails)
Examples
The examples folder contains working examples. You can run one of them with
PORT=8080 npm run examples:01_minimal
and open http://localhost:8080 in your web browser.
You can also try them in codesandbox.io: 01 02
Related projects
6 days ago
2 months ago
2 months ago
2 months ago
2 years ago
2 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago