1.4.4 • Published 6 days ago

use-context-selector v1.4.4

Weekly downloads
16,395
License
MIT
Repository
github
Last release
6 days ago

use-context-selector

Build Status npm version bundle size

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, default identity 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 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 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

jotai-newvspot-formcra-template-rq09-dark-mode-selectorproste-react-hooksproste-taro-hooks@infinitebrahmanuniverse/nolb-use-ccra-template-rq10-dark-mode-selector@textea/yodas@everything-registry/sub-chunk-3025hello-world-1807@blessmesanta/react-spreadsheet@barnij/react-spreadsheet@brainhive/strapi-admin@toktokhan-fe/react-universal@toktokhan-fe/template-next-page-router@toktokhan-fe/utils@silicon-jungle/fork-react-spreadsheet@astrohaus/freewrite-ui@cig-platform/contextdabsi-payloaddigital-human-chatbot-js@emilgpa/fomu@eduzz/houston-ui@eduzz/ui-layout@dynamic-layout/react@fyralabs/state@googleforcreators/react@hermes/react@filerobot/explorer@filerobot/provider-views@yoimu/react-common-lib@iteria-app/component-templates@toktokhan-fe/next-page-routerfluxisaurfomupayload-custompayload-with-decoratorpayloadddjohnn2o-frameworkperso-corepayload-johnproste-react-user3f-configreact-immersivereact-chat-potatoreact-connect-context-hooksreact-context-toolkitreact-create-hook-contextreact-hooks-fetchreact-best-tablereact-better-tablereact-spreadsheetreact-spreadsheet-customreact-spreadsheet-highlightreact-store-toolkitreact-state-firereact-trackedrecontextualreducer-kitreact-pretty-contextresponsive-nativets5-framework@sandpack-monaco/reactuse-atomuse_reducer_simple_syntax@ray-js/sdm-react@ray-js/panel-sdk@serusko/reform@tensile-perf/reactwygininc@adailsonaguiar/schedule-platform@andreciornavei/mui-boost@antmjs/global-state@vo2-brand-experience/strapi-admin@strapi/admin@layout-flow/react@lnminh0710/price-tag@lyn4ed/strapi-admin@lynched-test/strapi-admin@lynxts/core@innatical/innstate@mdshad/react-spreadsheet@meeehdi/strapi-admin@netdata/netdata-ui@netdata/dashboard@nikolarhristov/payload@oro-dxeco/react-spreadsheet@opensea/ui-kit@phen0menon/react-spreadsheet@payloadcms/uichayns-api@qoretechnologies/reqore@qoretechnologies/reqraft@qorebase/app-runtime@r1tsu/ui
2.0.0-beta.0

6 days ago

1.4.4

2 months ago

1.4.3

2 months ago

1.4.2

2 months ago

1.4.1

2 years ago

1.4.0

2 years ago

1.3.10

2 years ago

1.3.9

3 years ago

1.3.8

3 years ago

1.3.7

3 years ago

1.3.6

3 years ago

1.3.5

3 years ago

1.3.4

3 years ago

1.3.3

3 years ago

1.3.2

3 years ago

1.3.1

3 years ago

1.3.0

3 years ago

1.2.12

3 years ago

1.2.11

3 years ago

1.2.10

4 years ago

1.2.9

4 years ago

1.2.8

4 years ago

1.2.7

4 years ago

1.2.6

4 years ago

1.2.5

4 years ago

1.2.4

4 years ago

1.2.3

4 years ago

1.2.2

4 years ago

1.2.0

4 years ago

1.2.1

4 years ago

2.0.0-alpha.9

4 years ago

1.1.4

4 years ago

1.1.4-beta.2

4 years ago

1.1.4-beta.1

4 years ago

1.1.3

4 years ago

2.0.0-alpha.7

4 years ago

2.0.0-alpha.6

4 years ago

2.0.0-alpha.5

4 years ago

1.1.2

4 years ago

2.0.0-alpha.4

4 years ago

2.0.0-alpha.3

4 years ago

2.0.0-alpha.2

4 years ago

2.0.0-alpha.1

4 years ago

1.1.1

4 years ago

1.1.0

4 years ago

1.0.1

5 years ago

1.0.0

5 years ago

0.4.0

5 years ago

0.3.0

5 years ago

0.2.0

5 years ago

0.1.0

5 years ago