Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

use-context-selector

Package Overview
Dependencies
Maintainers
1
Versions
52
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

use-context-selector - npm Package Compare versions

Comparing version 2.0.0-alpha.9 to 2.0.0-beta.0

dist/cjs/index.d.ts

106

package.json
{
"name": "use-context-selector",
"description": "React useContext with selector support in userland",
"version": "2.0.0-alpha.9",
"description": "React useContextSelector hook in userland",
"version": "2.0.0-beta.0",
"publishConfig": {
"tag": "next"
},
"type": "module",
"author": "Daishi Kato",

@@ -14,5 +15,17 @@ "repository": {

"source": "./src/index.ts",
"main": "./dist/index.umd.js",
"module": "./dist/index.modern.js",
"types": "./dist/src/index.d.ts",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
"./package.json": "./package.json",
".": {
"require": {
"types": "./dist/cjs/index.d.ts",
"default": "./dist/cjs/index.js"
},
"default": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
}
},
"sideEffects": false,

@@ -23,11 +36,17 @@ "files": [

],
"packageManager": "pnpm@8.15.0",
"scripts": {
"compile": "microbundle build -f modern,umd",
"test": "run-s eslint tsc-test jest",
"eslint": "eslint --ext .js,.ts,.tsx --ignore-pattern dist .",
"jest": "jest --preset ts-jest/presets/js-with-ts __tests__/*.tsx",
"tsc-test": "tsc --project . --noEmit",
"apidoc": "documentation readme --section API --markdown-toc false --parse-extension ts src/*.ts",
"examples:01_minimal": "DIR=01_minimal EXT=js webpack-dev-server",
"examples:02_typescript": "DIR=02_typescript webpack-dev-server"
"compile": "rm -rf dist && pnpm run '/^compile:.*/'",
"compile:esm": "tsc -p tsconfig.esm.json",
"compile:cjs": "tsc -p tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",
"test": "pnpm run '/^test:.*/'",
"test:format": "prettier -c .",
"test:lint": "eslint .",
"test:types": "tsc -p . --noEmit",
"test:types:examples": "tsc -p examples --noEmit",
"test:spec": "vitest run",
"apidoc": "documentation readme src/index.ts --section API --markdown-toc false --parse-extension ts --require-extension ts",
"examples:01_counter": "DIR=01_counter vite",
"examples:02_person": "DIR=02_person vite",
"examples:03_suspense": "DIR=03_suspense vite"
},

@@ -40,36 +59,37 @@ "keywords": [

"license": "MIT",
"dependencies": {},
"prettier": {
"singleQuote": true
},
"devDependencies": {
"@testing-library/react": "^10.4.7",
"@types/jest": "^26.0.8",
"@types/react": "^16.9.44",
"@types/react-dom": "^16.9.8",
"@types/scheduler": "^0.16.1",
"@typescript-eslint/eslint-plugin": "^3.7.1",
"@typescript-eslint/parser": "^3.7.1",
"documentation": "^13.0.2",
"eslint": "^7.6.0",
"eslint-config-airbnb": "^18.2.0",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-jsx-a11y": "^6.3.1",
"eslint-plugin-react": "^7.20.5",
"eslint-plugin-react-hooks": "^4.0.8",
"html-webpack-plugin": "^4.3.0",
"jest": "^26.2.2",
"microbundle": "^0.12.3",
"npm-run-all": "^4.1.5",
"react": "experimental",
"react-dom": "experimental",
"scheduler": "experimental",
"ts-jest": "^26.1.4",
"ts-loader": "^8.0.2",
"typescript": "^3.9.7",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0"
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^15.0.5",
"@testing-library/user-event": "^14.5.2",
"@types/node": "^20.12.7",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
"@types/scheduler": "^0.23.0",
"@typescript-eslint/eslint-plugin": "^7.7.1",
"@typescript-eslint/parser": "^7.7.1",
"documentation": "^14.0.3",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.2",
"happy-dom": "^14.7.1",
"prettier": "^3.2.5",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"scheduler": "^0.23.2",
"ts-expect": "^1.3.0",
"typescript": "^5.4.5",
"vite": "^5.2.10",
"vitest": "^1.5.2"
},
"peerDependencies": {
"react": ">=16.14.0",
"scheduler": ">=0.20.0"
"react": ">=18.0.0",
"scheduler": ">=0.19.0"
}
}
# use-context-selector
[![CI](https://img.shields.io/github/workflow/status/dai-shi/use-context-selector/CI)](https://github.com/dai-shi/use-context-selector/actions?query=workflow%3ACI)
[![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 useContext with selector support in userland
React useContextSelector hook in userland

@@ -23,16 +24,31 @@ ## Introduction

v1 uses `calculateChangedBits=()=>0` technique to stop propagation,
while v2 uses `useMutableSource`.
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
yarn add 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.
## Usage
```javascript
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import { useState } from 'react';
import { createRoot } from 'react-dom/client';

@@ -44,12 +60,15 @@ import { createContext, useContextSelector } from 'use-context-selector';

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,
}));
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>
<button type="button" onClick={increment}>
+1
</button>
{Math.random()}

@@ -61,12 +80,15 @@ </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,
}));
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>
<button type="button" onClick={increment}>
+1
</button>
{Math.random()}

@@ -77,10 +99,7 @@ </div>

const StateProvider = ({ children }) => {
const [state, setState] = useState({ count1: 0, count2: 0 });
return (
<context.Provider value={[state, setState]}>
{children}
</context.Provider>
);
};
const StateProvider = ({ children }) => (
<context.Provider value={useState({ count1: 0, count2: 0 })}>
{children}
</context.Provider>
);

@@ -94,21 +113,5 @@ const App = () => (

ReactDOM.render(<App />, document.getElementById('app'));
createRoot(document.getElementById('app')).render(<App />);
```
## Migrating from v1 to v2
In v1:
```js
useContextSelector(context, state => state.count);
```
In v2:
```js
useContext(context, useCallback(state => state.count, []));
```
In this case, you can (should) also define the selector function outside render.
## API

@@ -120,11 +123,7 @@

This creates a special context for selector-enabled `useContext`.
This creates a special context for `useContextSelector`.
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**
* `defaultValue` **Value**&#x20;

@@ -139,10 +138,8 @@ #### Examples

### useContext
### useContextSelector
This hook returns context value with optional selector.
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 must be stable.
Either define selector outside render or wrap with `useCallback`.

@@ -153,4 +150,4 @@ The selector should return referentially equal result for same input for better performance.

- `context` **Context&lt;Value>**
- `selector` **function (value: Value): Selected** (optional, default `identity as(value:Value)=>Selected`)
* `context` **Context\<Value>**&#x20;
* `selector` **function (value: Value): Selected**&#x20;

@@ -160,5 +157,22 @@ #### 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\<Value>**&#x20;
#### Examples
```javascript
import { useContext } from 'use-context-selector';
const firstName = useContext(PersonContext, state => state.firstName);
const person = useContext(PersonContext);
```

@@ -168,9 +182,11 @@

This hook returns an update function that accepts a thunk function
This hook returns an update function to wrap an updating function
Use this for a function that will change a value.
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&lt;any>**
* `context` **Context\<Value>**&#x20;

@@ -183,3 +199,8 @@ #### Examples

const update = useContextUpdate();
// Wrap set state function
update(() => setState(...));
// Experimental suspense mode
update(() => setState(...), { suspense: true });
```

@@ -191,15 +212,14 @@

Type: React.FC&lt;{context: Context&lt;any>, value: any}>
#### Parameters
- `$0` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)**
- `$0.context`
- `$0.value`
- `$0.children`
* `$0` **{context: Context\<any>, value: any, children: ReactNode}**&#x20;
* `$0.context` &#x20;
* `$0.value` &#x20;
* `$0.children` &#x20;
#### Examples
```javascript
const valueToBridge = useContext(PersonContext);
const valueToBridge = useBridgeValue(PersonContext);
return (

@@ -214,8 +234,17 @@ <Renderer>

### useBridgeValue
This hook return a value for BridgeProvider
#### Parameters
* `context` **Context\<any>**&#x20;
## 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`.
- Neither context consumers or class components are supported.
- The [stale props](https://react-redux.js.org/api/hooks#stale-props-and-zombie-children) 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)
* 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 can't be solved in userland.
* 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)

@@ -228,3 +257,3 @@ ## Examples

```bash
PORT=8080 npm run examples:01_minimal
PORT=8080 yarn run examples:01_counter
```

@@ -235,8 +264,10 @@

You can also try them in codesandbox.io:
[01](https://codesandbox.io/s/github/dai-shi/use-context-selector/tree/master/examples/01_minimal)
[02](https://codesandbox.io/s/github/dai-shi/use-context-selector/tree/master/examples/02_typescript)
[01](https://codesandbox.io/s/github/dai-shi/use-context-selector/tree/main/examples/01_counter)
[02](https://codesandbox.io/s/github/dai-shi/use-context-selector/tree/main/examples/02_person)
[03](https://codesandbox.io/s/github/dai-shi/use-context-selector/tree/main/examples/03_suspense)
## Related projects
## Projects that use use-context-selector
- [react-tracked](https://github.com/dai-shi/react-tracked)
- [reactive-react-redux](https://github.com/dai-shi/reactive-react-redux)
* [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)

@@ -1,59 +0,68 @@

/* eslint-disable @typescript-eslint/ban-ts-comment */
import {
ComponentType,
Context as ContextOrig,
FC,
MutableRefObject,
Provider,
createElement,
createContext as createContextOrig,
// @ts-ignore
unstable_createMutableSource as createMutableSource,
useCallback,
useContext as useContextOrig,
useEffect,
useLayoutEffect,
useMemo,
// @ts-ignore
unstable_useMutableSource as useMutableSource,
useReducer,
useRef,
useState,
} from 'react';
import type {
ComponentType,
Context as ContextOrig,
MutableRefObject,
Provider,
ReactNode,
} from 'react';
import {
unstable_UserBlockingPriority as UserBlockingPriority,
unstable_NormalPriority as NormalPriority,
unstable_runWithPriority as runWithPriority,
unstable_getCurrentPriorityLevel as getCurrentPriorityLevel,
} from 'scheduler';
const isSSR = typeof window === 'undefined'
|| /ServerSideRendering/.test(window.navigator && window.navigator.userAgent);
const CONTEXT_VALUE = Symbol();
const ORIGINAL_PROVIDER = Symbol();
const useIsoLayoutEffect = isSSR
? (fn: () => void) => fn()
: useLayoutEffect;
const isSSR =
typeof window === 'undefined' ||
/ServerSideRendering/.test(window.navigator && window.navigator.userAgent);
const SOURCE_SYMBOL = Symbol();
const UPDATE_SYMBOL = Symbol();
const useIsomorphicLayoutEffect = isSSR ? useEffect : useLayoutEffect;
const FUNCTION_SYMBOL = Symbol();
// eslint-disable-next-line @typescript-eslint/ban-types
const functionMap = new WeakMap<Function, { [FUNCTION_SYMBOL]: Function }>();
// for preact that doesn't have runWithPriority
const runWithNormalPriority = runWithPriority
? (fn: () => void) => {
try {
runWithPriority(NormalPriority, fn);
} catch (e) {
if ((e as { message: unknown }).message === 'Not implemented.') {
fn();
} else {
throw e;
}
}
}
: (fn: () => void) => fn();
const ORIGINAL_PROVIDER = Symbol();
type Version = number;
type Listener<Value> = (action: {
n: Version;
p?: Promise<Value>;
v?: Value;
}) => void;
// @ts-ignore
type ContextValue<Value> = {
[SOURCE_SYMBOL]: any;
[UPDATE_SYMBOL]: <T>(thunk: () => T) => T;
[CONTEXT_VALUE]: {
/* "v"alue */ v: MutableRefObject<Value>;
/* versio"n" */ n: MutableRefObject<Version>;
/* "l"isteners */ l: Set<Listener<Value>>;
/* "u"pdate */ u: (
fn: () => void,
options?: { suspense: boolean },
) => void;
};
};
type RefValue<Value> = MutableRefObject<{
v: number; // "v" = version
p: Value; // "p" = primary value
s: Value; // "s" = secondary value
l: Set<() => void>; // "l" = listeners
}>;
export interface Context<Value> {
Provider: ComponentType<{ value: Value }>;
Provider: ComponentType<{ value: Value; children: ReactNode }>;
displayName?: string;

@@ -63,32 +72,64 @@ }

const createProvider = <Value>(ProviderOrig: Provider<ContextValue<Value>>) => {
const RefProvider: FC<{ value: Value }> = ({ value, children }) => {
const ref: RefValue<Value> = useRef({
v: 0, // "v" = version
p: value, // "p" = primary value
s: value, // "s" = secondary value
l: new Set<() => void>(), // "l" = listeners
});
const priorityRef = useRef(NormalPriority);
const contextValue = useMemo(() => ({
[SOURCE_SYMBOL]: createMutableSource(ref, () => ref.current.v),
[UPDATE_SYMBOL]: <T>(thunk: () => T) => {
priorityRef.current = getCurrentPriorityLevel();
return runWithPriority(UserBlockingPriority, thunk);
},
}), []);
useIsoLayoutEffect(() => {
if (contextValue[SOURCE_SYMBOL]._workInProgressVersionSecondary !== null) {
ref.current.s = value; // update secondary value
} else {
ref.current.p = value; // update primary value
}
ref.current.v += 1; // increment version
runWithPriority(priorityRef.current, () => {
ref.current.l.forEach((listener) => listener());
const ContextProvider = ({
value,
children,
}: {
value: Value;
children: ReactNode;
}) => {
const valueRef = useRef(value);
const versionRef = useRef(0);
const [resolve, setResolve] = useState<((v: Value) => void) | null>(null);
if (resolve) {
resolve(value);
setResolve(null);
}
const contextValue = useRef<ContextValue<Value>>();
if (!contextValue.current) {
const listeners = new Set<Listener<Value>>();
const update = (fn: () => void, options?: { suspense: boolean }) => {
versionRef.current += 1;
const action: Parameters<Listener<Value>>[0] = {
n: versionRef.current,
};
if (options?.suspense) {
action.n *= -1; // this is intentional to make it temporary version
action.p = new Promise<Value>((r) => {
setResolve(() => (v: Value) => {
action.v = v;
delete action.p;
r(v);
});
});
}
listeners.forEach((listener) => listener(action));
fn();
};
contextValue.current = {
[CONTEXT_VALUE]: {
/* "v"alue */ v: valueRef,
/* versio"n" */ n: versionRef,
/* "l"isteners */ l: listeners,
/* "u"pdate */ u: update,
},
};
}
useIsomorphicLayoutEffect(() => {
valueRef.current = value;
versionRef.current += 1;
runWithNormalPriority(() => {
(contextValue.current as ContextValue<Value>)[CONTEXT_VALUE].l.forEach(
(listener) => {
listener({ n: versionRef.current, v: value });
},
);
});
priorityRef.current = NormalPriority;
});
return createElement(ProviderOrig, { value: contextValue }, children);
}, [value]);
return createElement(
ProviderOrig,
{ value: contextValue.current },
children,
);
};
return RefProvider;
return ContextProvider;
};

@@ -98,21 +139,5 @@

const createDefaultSource = <Value>(defaultValue: Value) => {
const ref: RefValue<Value> = {
current: {
v: -1, // "v" = version
p: defaultValue, // "p" = primary value
s: defaultValue, // "s" = secondary value
l: new Set<() => void>(), // "l" = listeners
},
};
return createMutableSource(ref, () => ref.current.v);
};
/**
* This creates a special context for selector-enabled `useContext`.
* This creates a special context for `useContextSelector`.
*
* 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.
*
* @example

@@ -125,35 +150,26 @@ * import { createContext } from 'use-context-selector';

const context = createContextOrig<ContextValue<Value>>({
[SOURCE_SYMBOL]: createDefaultSource(defaultValue),
[UPDATE_SYMBOL]: (thunk) => thunk(),
[CONTEXT_VALUE]: {
/* "v"alue */ v: { current: defaultValue },
/* versio"n" */ n: { current: -1 },
/* "l"isteners */ l: new Set(),
/* "u"pdate */ u: (f) => f(),
},
});
(context as unknown as {
[ORIGINAL_PROVIDER]: Provider<ContextValue<Value>>;
})[ORIGINAL_PROVIDER] = context.Provider;
(context as unknown as Context<Value>).Provider = createProvider(context.Provider);
delete context.Consumer; // no support for Consumer
(
context as unknown as {
[ORIGINAL_PROVIDER]: Provider<ContextValue<Value>>;
}
)[ORIGINAL_PROVIDER] = context.Provider;
(context as unknown as Context<Value>).Provider = createProvider(
context.Provider,
);
delete (context as { Consumer: unknown }).Consumer; // no support for Consumer
return context as unknown as Context<Value>;
}
const subscribe = (
ref: RefValue<unknown>,
callback: () => void,
) => {
const listeners = ref.current.l;
listeners.add(callback);
return () => listeners.delete(callback);
};
export function useContext<Value>(context: Context<Value>): Value
export function useContext<Value, Selected>(
context: Context<Value>,
selector: (value: Value) => Selected,
): Selected
/**
* This hook returns context value with optional selector.
* 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 must be stable.
* Either define selector outside render or wrap with `useCallback`.
*

@@ -163,48 +179,94 @@ * The selector should return referentially equal result for same input for better performance.

* @example
* import { useContext } from 'use-context-selector';
* import { useContextSelector } from 'use-context-selector';
*
* const firstName = useContext(PersonContext, state => state.firstName);
* const firstName = useContextSelector(PersonContext, (state) => state.firstName);
*/
export function useContext<Value, Selected>(
export function useContextSelector<Value, Selected>(
context: Context<Value>,
selector: (value: Value) => Selected = identity as (value: Value) => Selected,
selector: (value: Value) => Selected,
) {
const { [SOURCE_SYMBOL]: source } = useContextOrig(
const contextValue = useContextOrig(
context as unknown as ContextOrig<ContextValue<Value>>,
);
if (process.env.NODE_ENV !== 'production') {
if (!source) {
throw new Error('This useContext requires special context for selector support');
)[CONTEXT_VALUE];
if (typeof process === 'object' && process.env.NODE_ENV !== 'production') {
if (!contextValue) {
throw new Error('useContextSelector requires special context');
}
}
const getSnapshot = useCallback(
(ref: RefValue<Value>) => {
const value = source._workInProgressVersionSecondary !== null
? ref.current.s // "s" = secondary value
: ref.current.p; // "p" = primary value
const selected = selector(value);
if (typeof selected === 'function') {
if (functionMap.has(selected)) {
return functionMap.get(selected);
const {
/* "v"alue */ v: { current: value },
/* versio"n" */ n: { current: version },
/* "l"isteners */ l: listeners,
} = contextValue;
const selected = selector(value);
const [state, dispatch] = useReducer(
(
prev: readonly [Value, Selected],
action?: Parameters<Listener<Value>>[0],
) => {
if (!action) {
// case for `dispatch()` below
return [value, selected] as const;
}
if ('p' in action) {
throw action.p;
}
if (action.n === version) {
if (Object.is(prev[1], selected)) {
return prev; // bail out
}
const wrappedFunction = { [FUNCTION_SYMBOL]: selected };
functionMap.set(selected, wrappedFunction);
return wrappedFunction;
return [value, selected] as const;
}
return selected;
try {
if ('v' in action) {
if (Object.is(prev[0], action.v)) {
return prev; // do not update
}
const nextSelected = selector(action.v);
if (Object.is(prev[1], nextSelected)) {
return prev; // do not update
}
return [action.v, nextSelected] as const;
}
} catch (_e) {
// ignored (stale props or some other reason)
}
return [...prev] as const; // schedule update
},
[selector, source],
[value, selected] as const,
);
const snapshot = useMutableSource(source, getSnapshot, subscribe);
if (snapshot && (snapshot as { [FUNCTION_SYMBOL]: unknown })[FUNCTION_SYMBOL]) {
return snapshot[FUNCTION_SYMBOL];
if (!Object.is(state[1], selected)) {
// schedule re-render
// this is safe because it's self contained
dispatch();
}
return snapshot;
useIsomorphicLayoutEffect(() => {
listeners.add(dispatch);
return () => {
listeners.delete(dispatch);
};
}, [listeners]);
return state[1];
}
/**
* This hook returns an update function that accepts a thunk function
* This hook returns the entire context value.
* Use this instead of React.useContext for consistent behavior.
*
* Use this for a function that will change a value.
* @example
* import { useContext } from 'use-context-selector';
*
* const person = useContext(PersonContext);
*/
export function useContext<Value>(context: Context<Value>) {
return useContextSelector(context, identity);
}
/**
* 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.
*
* @example

@@ -214,18 +276,24 @@ * import { useContextUpdate } from 'use-context-selector';

* const update = useContextUpdate();
*
* // Wrap set state function
* update(() => setState(...));
*
* // Experimental suspense mode
* update(() => setState(...), { suspense: true });
*/
export function useContextUpdate(
context: Context<unknown>,
) {
const { [UPDATE_SYMBOL]: update } = useContextOrig(
context as unknown as ContextOrig<ContextValue<unknown>>,
);
if (process.env.NODE_ENV !== 'production') {
if (!update) {
throw new Error('This useContext requires special context for selector support');
export function useContextUpdate<Value>(context: Context<Value>) {
const contextValue = useContextOrig(
context as unknown as ContextOrig<ContextValue<Value>>,
)[CONTEXT_VALUE];
if (typeof process === 'object' && process.env.NODE_ENV !== 'production') {
if (!contextValue) {
throw new Error('useContextUpdate requires special context');
}
}
const { u: update } = contextValue;
return update;
}
/* eslint-disable @typescript-eslint/no-explicit-any */
/**

@@ -235,3 +303,3 @@ * This is a Provider component for bridging multiple react roots

* @example
* const valueToBridge = useContext(PersonContext);
* const valueToBridge = useBridgeValue(PersonContext);
* return (

@@ -245,10 +313,15 @@ * <Renderer>

*/
export const BridgeProvider: React.FC<{
export const BridgeProvider = ({
context,
value,
children,
}: {
context: Context<any>;
value: any;
}> = ({ context, value, children }) => {
value: unknown;
children: ReactNode;
}) => {
const { [ORIGINAL_PROVIDER]: ProviderOrig } = context as unknown as {
[ORIGINAL_PROVIDER]: Provider<unknown>;
};
if (process.env.NODE_ENV !== 'production') {
if (typeof process === 'object' && process.env.NODE_ENV !== 'production') {
if (!ProviderOrig) {

@@ -260,1 +333,16 @@ throw new Error('BridgeProvider requires special context');

};
/**
* This hook return a value for BridgeProvider
*/
export const useBridgeValue = (context: Context<any>) => {
const bridgeValue = useContextOrig(
context as unknown as ContextOrig<ContextValue<unknown>>,
);
if (typeof process === 'object' && process.env.NODE_ENV !== 'production') {
if (!bridgeValue[CONTEXT_VALUE]) {
throw new Error('useBridgeValue requires special context');
}
}
return bridgeValue as any;
};

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc