Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/dai-shi/use-context-selector
React useContextSelector hook in userland
https://github.com/dai-shi/use-context-selector
custom-hook react react-context react-hooks
Last synced: 5 days ago
JSON representation
React useContextSelector hook in userland
- Host: GitHub
- URL: https://github.com/dai-shi/use-context-selector
- Owner: dai-shi
- License: mit
- Created: 2019-07-05T02:46:26.000Z (over 5 years ago)
- Default Branch: main
- Last Pushed: 2024-09-24T10:10:07.000Z (3 months ago)
- Last Synced: 2024-10-22T08:26:02.172Z (about 2 months ago)
- Topics: custom-hook, react, react-context, react-hooks
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/use-context-selector
- Size: 2.33 MB
- Stars: 2,645
- Watchers: 13
- Forks: 61
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
- fucking-awesome-react-hooks - `use-context-selector`
- awesome-react-hooks-cn - `use-context-selector`
- awesome-list - use-context-selector - shi | 1047 | (TypeScript)
- awesome-react-hooks - `use-context-selector`
- awesome-react-hooks - `use-context-selector`
README
# use-context-selector
[![CI](https://img.shields.io/github/actions/workflow/status/dai-shi/use-context-selector/ci.yml?branch=main)](https://github.com/dai-shi/use-context-selector/actions?query=workflow%3ACI)
[![npm](https://img.shields.io/npm/v/use-context-selector)](https://www.npmjs.com/package/use-context-selector)
[![size](https://img.shields.io/bundlephobia/minzip/use-context-selector)](https://bundlephobia.com/result?p=use-context-selector)
[![discord](https://img.shields.io/discord/627656437971288081)](https://discord.gg/MrQdmzd)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](https://github.com/reactjs/rfcs/pull/119)
is proposed and later proposed
[Speculative Mode](https://github.com/reactjs/rfcs/pull/150)
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
This package requires some peer dependencies, which you need to install by yourself.
```bash
npm install use-context-selector react scheduler
```Notes for library authors:
Please do not forget to keep `"peerDependencies"` and
note instructions to let users to install peer dependencies.## Technical memo
To make it work like original React context, it uses
[useReducer cheat mode](https://overreacted.io/a-complete-guide-to-useeffect/#why-usereducer-is-the-cheat-mode-of-hooks) intentionally.It also requires `useContextUpdate` to behave better in concurrent rendering.
Its usage is optional and only required if the default behavior is unexpected.If you need a simpler solution, you can use `useSyncExternalStore` without any libraries. See [an example](https://github.com/dai-shi/use-context-selector/issues/109#issuecomment-1785147682).
## Usage
```javascript
import { useState } from 'react';
import { createRoot } from 'react-dom/client';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 (
Count1: {count1}
+1
{Math.random()}
);
};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 (
Count2: {count2}
+1
{Math.random()}
);
};const StateProvider = ({ children }) => (
{children}
);const App = () => (
);createRoot(document.getElementById('app')).render();
```## API
### createContext
This creates a special context for `useContextSelector`.
#### Parameters
* `defaultValue` **Value**
#### Examples
```javascript
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\**
* `selector` **function (value: Value): Selected**#### Examples
```javascript
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\**
#### Examples
```javascript
import { useContext } from 'use-context-selector';const person = useContext(PersonContext);
```### useContextUpdate
This hook returns an update function to wrap an updating function
Use this for a function that will change a value in
concurrent rendering in React 18.
Otherwise, there's no need to use this hook.#### Parameters
* `context` **Context\**
#### Examples
```javascript
import { useContextUpdate } from 'use-context-selector';const update = useContextUpdate();
// Wrap set state function
update(() => setState(...));// Experimental suspense mode
update(() => setState(...), { suspense: true });
```### BridgeProvider
This is a Provider component for bridging multiple react roots
#### Parameters
* `$0` **{context: Context\, value: any, children: ReactNode}**
* `$0.context`
* `$0.value`
* `$0.children`#### Examples
```javascript
const valueToBridge = useBridgeValue(PersonContext);
return (
{children}
);
```### useBridgeValue
This hook return a value for BridgeProvider
#### Parameters
* `context` **Context\**
## Limitations
* In order to stop propagation, `children` of a context provider has to be either created outside of the provider or memoized with `React.memo`.
* Provider trigger re-renders only if the context value is referentially changed.
* Neither context consumers or class components are supported.
* The [stale props](https://react-redux.js.org/api/hooks#stale-props-and-zombie-children) issue exists in React 17 and below. (Can be resolved with `unstable_batchedUpdates`)
* Tearing is only avoided if all consumers get data using `useContextSelector`. If you use both props and `use-context-selector` to pass the same data, they may provide inconsistence data for a brief moment. (`02_tearing_spec` fails)## Examples
The [examples](examples) folder contains working examples.
You can run one of them with```bash
PORT=8080 pnpm run examples:01_counter
```and open in your web browser.
You can also try them directly:
[01](https://stackblitz.com/github/dai-shi/use-context-selector/tree/main/examples/01_counter)
[02](https://stackblitz.com/github/dai-shi/use-context-selector/tree/main/examples/02_person)
[03](https://stackblitz.com/github/dai-shi/use-context-selector/tree/main/examples/03_suspense)## Projects that use use-context-selector
* [react-tracked](https://github.com/dai-shi/react-tracked)
* [use-atom](https://github.com/dai-shi/use-atom)
* [react-hooks-fetch](https://github.com/dai-shi/react-hooks-fetch)