JSPM

  • Created
  • Published
  • Downloads 569531
  • Score
    100M100P100Q193913F
  • License MIT

React useContextSelector hook in userland

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

CI npm size

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.

useContextSelector is proposed. While waiting for the process, this library provides the API in userland.

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'));

Technical memo

React context by nature triggers propagation of component re-rendering if a value is changed. To avoid this, this library uses undocumented feature of calculateChangedBits. It then uses a subscription model to force update when a component needs to re-render.

API

createContext

This creates a special context for useContextSelector.

Parameters

  • defaultValue any

Examples

const PersonContext = createContext({ firstName: '', familyName: '' });

Returns React.Context

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.

Parameters

  • context React.Context
  • selector Function

Examples

const firstName = useContextSelector(PersonContext, state => state.firstName);

Returns any

useContext

This hook returns the entire context value. Use this instead of React.useContext for consistent behavior.

Parameters

  • context React.Context

Examples

const person = useContext(PersonContext);

Returns any

useContextUpdate

This hook returns an update function that accepts a thunk function

Use this for a function that will change a value.

Parameters

  • context

Examples

import { useContextUpdate } from 'use-context-selector';

const update = useContextUpdate();
update(() => setState(...));

BridgeProvider

This is a Provider component for bridging multiple react roots

Parameters

  • props Object
    • props.context React.Context
    • props.value any
    • props.children React.ReactNote

Examples

const valueToBridge = useBridgeValue(PersonContext);
return (
  <Renderer>
    <BridgeProvider context={PersonContext} value={valueToBridge}>
      {children}
    </BridgeProvider>
  </Renderer>
);

Returns React.ReactElement

useBridgeValue

This hook return a value for BridgeProvider

Parameters

  • context React.Context

Returns any

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.
  • Context consumers are not supported.
  • The stale props issue can't be solved in userland. (workaround with try-catch)

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