Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork70
State usage tracking with Proxies. Optimize re-renders for useState/useReducer, React Redux, Zustand and others.
License
dai-shi/react-tracked
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
State usage tracking with Proxies. Optimize re-renders for useState/useReducer, React Redux, Zustand and others.
Documentation site:https://react-tracked.js.org
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 inmany 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 referenceequality which is non-trival for beginners andexperts 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 forthe root level of the object but also for deep nested objects.
Prior to v1.6.0, React Tracked is a library to replace React Contextuse cases for global state. React hook useContext triggers re-renderswhenever a small part of state object is changed, and it would causeperformance issues pretty easily. React Tracked provides an APIthat is very similar to useContext-style global state.
Since v1.6.0, it provides another building-block APIwhich is capable to create a "state usage tracking" hooksfrom any selector interface hooks.It can be used with React-Redux useSelector, and any other librariesthat provide useSelector-like hooks.
This package requires some peer dependencies, which you need to install by yourself.
npm install react-tracked react scheduler
There are two main APIscreateContainer andcreateTrackedSelector.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.
import{useState}from'react';constuseValue=()=>useState({count:0,text:'hello',});
This can be useReducer or any hook that returns a tuple[state, dispatch].
import{createContainer}from'react-tracked';const{ Provider, useTracked}=createContainer(useValue);
constCounter=()=>{const[state,setState]=useTracked();constincrement=()=>{setState((prev)=>({ ...prev,count:prev.count+1,}));};return(<div><span>Count:{state.count}</span><buttontype="button"onClick={increment}> +1</button></div>);};
TheuseTracked hook returns a tuple thatuseValue returns,except that the first is the state wrapped by proxies andthe second part is a wrapped function for a reason.
Thanks to proxies, the property access in render is tracked andthis component will re-render only ifstate.count is changed.
constApp=()=>(<Provider><Counter/><TextBox/></Provider>);
import{useSelector,useDispatch}from'react-redux';import{createTrackedSelector}from'react-tracked';constuseTrackedSelector=createTrackedSelector(useSelector);
constCounter=()=>{conststate=useTrackedSelector();constdispatch=useDispatch();return(<div><span>Count:{state.count}</span><buttontype="button"onClick={()=>dispatch({type:'increment'})}> +1</button></div>);};
importcreatefrom'zustand';constuseStore=create(()=>({count:0}));
import{createTrackedSelector}from'react-tracked';constuseTrackedStore=createTrackedSelector(useStore);
constCounter=()=>{conststate=useTrackedStore();constincrement=()=>{useStore.setState((prev)=>({count:prev.count+1}));};return(<div><span>Count:{state.count}</span><buttontype="button"onClick={increment}> +1</button></div>);};
This library internally usesuse-context-selector,a userland solution foruseContextSelector hook.React 18 changes useReducer behavior whichuse-context-selector depends on.This may cause an unexpected behavior for developers.If you see moreconsole.log logs than expected,you may want to try puttingconsole.log in useEffect.If that shows logs as expected, it's an expected behavior.For more information:
https://github.com/dai-shi/lets-compare-global-state-with-react-hooks
Theexamples folder contains working examples.You can run one of them with
PORT=8080 pnpm run examples:01_minimal
and openhttp://localhost:8080 in your web browser.
You can also try them directly:01020304050607080910111213
Seethis for details.
- Super performant global state with React context and hooks
- Redux-less context-based useSelector hook that has same performance as React-Redux
- Four different approaches to non-Redux global state libraries
- What is state usage tracking? A novel approach to intuitive and performant global state with React hooks and Proxy
- How to use react-tracked: React hooks-oriented Todo List example
- Effortless render optimization with state usage tracking with React hooks
- 4 options to prevent extra rerenders with React context
- React Tracked Documentation Website with Docusaurus v2
About
State usage tracking with Proxies. Optimize re-renders for useState/useReducer, React Redux, Zustand and others.
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Sponsor this project
Uh oh!
There was an error while loading.Please reload this page.
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Contributors13
Uh oh!
There was an error while loading.Please reload this page.