Package Exports
- use-context-selector
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (use-context-selector) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
use-context-selector
React useContextSelector hook 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.
Prior to v1.3, it uses changedBits=0
feature to stop propagation,
v1.3 no longer depends on this undocumented feature.
Install
npm install use-context-selector
Technical memo
To make it work like original React context, it uses
useReducer cheat mode intentionally.
It also requires useContextUpdate
to behave better in Concurrent Mode.
(You don't need to use it in Legacy Mode.)
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'));
API
createContext
This creates a special context for useContextSelector
.
Parameters
defaultValue
Value
Examples
import { createContext } from 'use-context-selector';
const PersonContext = createContext({ firstName: '', familyName: '' });
useContextSelector
This hook returns context selected value by selector.
It will only accept context created by createContext
.
It will trigger re-render if only the selected value is referentially changed.
The selector should return referentially equal result for same input for better performance.
Parameters
context
Context<Value>selector
function (value: Value): Selected
Examples
import { useContextSelector } from 'use-context-selector';
const firstName = useContextSelector(PersonContext, state => state.firstName);
useContext
This hook returns the entire context value. Use this instead of React.useContext for consistent behavior.
Parameters
context
Context<Value>
Examples
import { useContext } from 'use-context-selector';
const person = useContext(PersonContext);
useContextUpdate
This hook returns an update function that accepts a thunk function
Use this for a function that will change a value.
Parameters
context
Context<Value>
Examples
import { useContextUpdate } from 'use-context-selector';
const update = useContextUpdate();
update(() => setState(...));
BridgeProvider
This is a Provider component for bridging multiple react roots
Type: FC<{context: Context<any>, value: any}>
Parameters
$0
Object$0.context
$0.value
$0.children
Examples
const valueToBridge = useBridgeValue(PersonContext);
return (
<Renderer>
<BridgeProvider context={PersonContext} value={valueToBridge}>
{children}
</BridgeProvider>
</Renderer>
);
useBridgeValue
This hook return a value for BridgeProvider
Parameters
context
Context<any>
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 can't be solved in userland.
- 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