react-tracked
    TypeScript icon, indicating that this package has built-in type declarations

    1.7.6 • Public • Published

    logo

    React Tracked

    CI npm size discord

    State usage tracking with Proxies. Optimize re-renders for useState/useReducer, React Redux, Zustand and others.

    Documentation site: https://react-tracked.js.org

    Introduction

    Preventing re-renders is one of performance issues in React. Smaller apps wouldn't usually suffer from such a performance issue, but once apps have a central global state that would be used in many components. The performance issue would become a problem. For example, Redux is usually used for a single global state, and React-Redux provides a selector interface to solve the performance issue. Selectors are useful to structure state accessor, however, using selectors only for performance wouldn't be the best fit. Selectors for performance require understanding object reference equality which is non-trival for beginners and experts would still have difficulties for complex structures.

    React Tracked is a library to provide so-called "state usage tracking." It's a technique to track property access of a state object, and only triggers re-renders if the accessed property is changed. Technically, it uses Proxies underneath, and it works not only for the root level of the object but also for deep nested objects.

    Prior to v1.6.0, React Tracked is a library to replace React Context use cases for global state. React hook useContext triggers re-renders whenever a small part of state object is changed, and it would cause performance issues pretty easily. React Tracked provides an API that is very similar to useContext-style global state.

    Since v1.6.0, it provides another building-block API which is capable to create a "state usage tracking" hooks from any selector interface hooks. It can be used with React-Redux useSelector, and any other libraries that provide useSelector-like hooks.

    Install

    This package requires some peer dependencies, which you need to install by yourself.

    yarn add react-tracked react scheduler react-dom

    For React Native users:

    yarn add react-tracked react scheduler react-native

    Usage

    There are two main APIs createContainer and createTrackedSelector. Both take a hook as an input and return a hook (or a container including a hook).

    There could be various use cases. Here are some typical ones.

    createContainer / useState

    Define a useValue custom hook

    import { useState } from 'react';
    
    const useValue = () => useState({
      count: 0,
      text: 'hello',
    });

    This can be useReducer or any hook that returns a tuple [state, dispatch].

    Create a container

    import { createContainer } from 'react-tracked';
    
    const { Provider, useTracked } = createContainer(useValue);

    useTracked in a component

    const Counter = () => {
      const [state, setState] = useTracked();
      const increment = () => {
        setState((prev) => ({
          ...prev,
          count: prev.count + 1,
        });
      };
      return (
        <div>
          <span>Count: {state.count}</span>
          <button type="button" onClick={increment}>+1</button>
        </div>
      );
    };

    The useTracked hook returns a tuple that useValue returns, except that the first is the state wrapped by proxies and the second part is a wrapped function for a reason.

    Thanks to proxies, the property access in render is tracked and this component will re-render only if state.count is changed.

    Wrap your App with Provider

    const App = () => (
      <Provider>
        <Counter />
        <TextBox />
      </Provider>
    );

    createTrackedSelector / react-redux

    Create useTrackedSelector from useSelector

    import { useSelector, useDispatch } from 'react-redux';
    import { createTrackedSelector } from 'react-tracked';
    
    const useTrackedSelector = createTrackedSelector(useSelector);

    useTrackedSelector in a component

    const Counter = () => {
      const state = useTrackedSelector();
      const dispatch = useDispatch();
      return (
        <div>
          <span>Count: {state.count}</span>
          <button type="button" onClick={() => dispatch({ type: 'increment' })}>+1</button>
        </div>
      );
    };

    createTrackedSelector / zustand

    Create useStore

    import create from 'zustand';
    
    const useStore = create(() => ({ count: 0 }));

    Create useTrackedStore from useStore

    import { createTrackedSelector } from 'react-tracked';
    
    const useTrackedStore = createTrackedSelector(useStore);

    useTrackedStore in a component

    const Counter = () => {
      const state = useTrackedStore();
      const increment = () => {
        useStore.setState(prev => ({ count: prev.count + 1 }));
      };
      return (
        <div>
          <span>Count: {state.count}</span>
          <button type="button" onClick={increment}>+1</button>
        </div>
      );
    };

    API

    docs/api

    Recipes

    docs/recipes

    Caveats

    docs/caveats

    Related projects

    docs/comparison

    https://github.com/dai-shi/lets-compare-global-state-with-react-hooks

    Examples

    The examples folder contains working examples. You can run one of them with

    PORT=8080 yarn run examples:01_minimal

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

    You can also try them in codesandbox.io: 01 02 03 04 05 06 07 08 09 10 11 12 13

    Benchmarks

    See this for details.

    Blogs

    Install

    npm i react-tracked

    DownloadsWeekly Downloads

    7,775

    Version

    1.7.6

    License

    MIT

    Unpacked Size

    76.7 kB

    Total Files

    20

    Last publish

    Collaborators

    • daishi