npm

Need private packages and team management tools?Check out npm Orgs. »

use-context-selector

1.0.0 • Public • Published

use-context-selector

Build Status npm version bundle 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 recently 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 create 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 referencially 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

Limitations

  • Subscriptions are per-context basis. So, even if there are multiple context providers in a component tree, all components are subscribed to all providers. This may lead false positives (extra re-renders).
  • In order to stop propagation, children of a context provider has to be either created outside of the provider or memoized with React.memo.
  • 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:minimal

and open http://localhost:8080 in your web browser.

You can also try them in codesandbox.io: 01 02

Related projects

install

npm i use-context-selector

Downloadsweekly downloads

192

version

1.0.0

license

MIT

homepage

github.com

repository

Gitgithub

last publish

collaborators

  • avatar
Report a vulnerability