{"id":13450509,"url":"https://github.com/dai-shi/react-tracked","last_synced_at":"2025-05-12T13:11:09.377Z","repository":{"id":38706496,"uuid":"191557266","full_name":"dai-shi/react-tracked","owner":"dai-shi","description":"State usage tracking with Proxies. Optimize re-renders for useState/useReducer, React Redux, Zustand and others.","archived":false,"fork":false,"pushed_at":"2025-04-16T15:35:21.000Z","size":9591,"stargazers_count":2793,"open_issues_count":7,"forks_count":72,"subscribers_count":19,"default_branch":"main","last_synced_at":"2025-04-23T13:01:55.896Z","etag":null,"topics":["global-states","react","react-context","react-hooks","redux","zustand"],"latest_commit_sha":null,"homepage":"https://react-tracked.js.org","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dai-shi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["dai-shi"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2019-06-12T11:25:51.000Z","updated_at":"2025-04-18T09:07:26.000Z","dependencies_parsed_at":"2023-02-18T07:00:53.659Z","dependency_job_id":"59ad1a08-d501-4c3d-8cd7-b566ead9123f","html_url":"https://github.com/dai-shi/react-tracked","commit_stats":{"total_commits":445,"total_committers":13,"mean_commits":34.23076923076923,"dds":"0.15955056179775284","last_synced_commit":"9d552a04aebc93a807b47edbedfd1576acce73e9"},"previous_names":[],"tags_count":49,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dai-shi%2Freact-tracked","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dai-shi%2Freact-tracked/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dai-shi%2Freact-tracked/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dai-shi%2Freact-tracked/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dai-shi","download_url":"https://codeload.github.com/dai-shi/react-tracked/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253745163,"owners_count":21957317,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["global-states","react","react-context","react-hooks","redux","zustand"],"created_at":"2024-07-31T07:00:35.426Z","updated_at":"2025-05-12T13:11:07.915Z","avatar_url":"https://github.com/dai-shi.png","language":"TypeScript","funding_links":["https://github.com/sponsors/dai-shi"],"categories":["Packages","react","TypeScript"],"sub_categories":[],"readme":"[![logo](./website/static/img/react-tracked-logo-animated1.svg)](https://react-tracked.js.org)\n\n# React Tracked\n\n[![CI](https://img.shields.io/github/actions/workflow/status/dai-shi/react-tracked/ci.yml?branch=main)](https://github.com/dai-shi/react-tracked/actions?query=workflow%3ACI)\n[![npm](https://img.shields.io/npm/v/react-tracked)](https://www.npmjs.com/package/react-tracked)\n[![size](https://img.shields.io/bundlephobia/minzip/react-tracked)](https://bundlephobia.com/result?p=react-tracked)\n[![discord](https://img.shields.io/discord/627656437971288081)](https://discord.gg/MrQdmzd)\n\nState usage tracking with Proxies. Optimize re-renders for useState/useReducer, React Redux, Zustand and others.\n\nDocumentation site: https://react-tracked.js.org\n\n## Introduction\n\nPreventing re-renders is one of performance issues in React.\nSmaller apps wouldn't usually suffer from such a performance issue,\nbut once apps have a central global state that would be used in\nmany components. The performance issue would become a problem.\nFor example, Redux is usually used for a single global state,\nand React-Redux provides a selector interface to solve the performance issue.\nSelectors are useful to structure state accessor,\nhowever, using selectors only for performance wouldn't be the best fit.\nSelectors for performance require understanding object reference\nequality which is non-trival for beginners and\nexperts would still have difficulties for complex structures.\n\nReact Tracked is a library to provide so-called \"state usage tracking.\"\nIt's a technique to track property access of a state object,\nand only triggers re-renders if the accessed property is changed.\nTechnically, it uses Proxies underneath, and it works not only for\nthe root level of the object but also for deep nested objects.\n\nPrior to v1.6.0, React Tracked is a library to replace React Context\nuse cases for global state. React hook useContext triggers re-renders\nwhenever a small part of state object is changed, and it would cause\nperformance issues pretty easily. React Tracked provides an API\nthat is very similar to useContext-style global state.\n\nSince v1.6.0, it provides another building-block API\nwhich is capable to create a \"state usage tracking\" hooks\nfrom any selector interface hooks.\nIt can be used with React-Redux useSelector, and any other libraries\nthat provide useSelector-like hooks.\n\n## Install\n\nThis package requires some peer dependencies, which you need to install by yourself.\n\n```bash\nnpm install react-tracked react scheduler\n```\n\n## Usage\n\nThere are two main APIs `createContainer` and `createTrackedSelector`.\nBoth take a hook as an input and return a hook (or a container including a hook).\n\nThere could be various use cases. Here are some typical ones.\n\n### createContainer / useState\n\n#### Define a `useValue` custom hook\n\n```js\nimport { useState } from 'react';\n\nconst useValue = () =\u003e\n  useState({\n    count: 0,\n    text: 'hello',\n  });\n```\n\nThis can be useReducer or any hook that returns a tuple `[state, dispatch]`.\n\n#### Create a container\n\n```js\nimport { createContainer } from 'react-tracked';\n\nconst { Provider, useTracked } = createContainer(useValue);\n```\n\n#### useTracked in a component\n\n```jsx\nconst Counter = () =\u003e {\n  const [state, setState] = useTracked();\n  const increment = () =\u003e {\n    setState((prev) =\u003e ({\n      ...prev,\n      count: prev.count + 1,\n    }));\n  };\n  return (\n    \u003cdiv\u003e\n      \u003cspan\u003eCount: {state.count}\u003c/span\u003e\n      \u003cbutton type=\"button\" onClick={increment}\u003e\n        +1\n      \u003c/button\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\nThe `useTracked` hook returns a tuple that `useValue` returns,\nexcept that the first is the state wrapped by proxies and\nthe second part is a wrapped function for a reason.\n\nThanks to proxies, the property access in render is tracked and\nthis component will re-render only if `state.count` is changed.\n\n#### Wrap your App with Provider\n\n```jsx\nconst App = () =\u003e (\n  \u003cProvider\u003e\n    \u003cCounter /\u003e\n    \u003cTextBox /\u003e\n  \u003c/Provider\u003e\n);\n```\n\n### createTrackedSelector / react-redux\n\n#### Create `useTrackedSelector` from `useSelector`\n\n```js\nimport { useSelector, useDispatch } from 'react-redux';\nimport { createTrackedSelector } from 'react-tracked';\n\nconst useTrackedSelector = createTrackedSelector(useSelector);\n```\n\n#### useTrackedSelector in a component\n\n```jsx\nconst Counter = () =\u003e {\n  const state = useTrackedSelector();\n  const dispatch = useDispatch();\n  return (\n    \u003cdiv\u003e\n      \u003cspan\u003eCount: {state.count}\u003c/span\u003e\n      \u003cbutton type=\"button\" onClick={() =\u003e dispatch({ type: 'increment' })}\u003e\n        +1\n      \u003c/button\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n### createTrackedSelector / zustand\n\n#### Create useStore\n\n```js\nimport create from 'zustand';\n\nconst useStore = create(() =\u003e ({ count: 0 }));\n```\n\n#### Create `useTrackedStore` from `useStore`\n\n```js\nimport { createTrackedSelector } from 'react-tracked';\n\nconst useTrackedStore = createTrackedSelector(useStore);\n```\n\n#### useTrackedStore in a component\n\n```jsx\nconst Counter = () =\u003e {\n  const state = useTrackedStore();\n  const increment = () =\u003e {\n    useStore.setState((prev) =\u003e ({ count: prev.count + 1 }));\n  };\n  return (\n    \u003cdiv\u003e\n      \u003cspan\u003eCount: {state.count}\u003c/span\u003e\n      \u003cbutton type=\"button\" onClick={increment}\u003e\n        +1\n      \u003c/button\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n## Notes with React 18\n\nThis library internally uses `use-context-selector`,\na userland solution for `useContextSelector` hook.\nReact 18 changes useReducer behavior which `use-context-selector` depends on.\nThis may cause an unexpected behavior for developers.\nIf you see more `console.log` logs than expected,\nyou may want to try putting `console.log` in useEffect.\nIf that shows logs as expected, it's an expected behavior.\nFor more information:\n\n- https://github.com/dai-shi/use-context-selector/issues/100\n- https://github.com/dai-shi/react-tracked/issues/177\n\n## API\n\n[docs/api](./website/docs/api.md)\n\n## Recipes\n\n[docs/recipes](./website/docs/recipes.md)\n\n## Caveats\n\n[docs/caveats](./website/docs/caveats.md)\n\n## Related projects\n\n[docs/comparison](./website/docs/comparison.md)\n\n\u003chttps://github.com/dai-shi/lets-compare-global-state-with-react-hooks\u003e\n\n## Examples\n\nThe [examples](examples) folder contains working examples.\nYou can run one of them with\n\n```bash\nPORT=8080 pnpm run examples:01_minimal\n```\n\nand open \u003chttp://localhost:8080\u003e in your web browser.\n\nYou can also try them directly:\n[01](https://stackblitz.com/github/dai-shi/react-tracked/tree/main/examples/01_minimal)\n[02](https://stackblitz.com/github/dai-shi/react-tracked/tree/main/examples/02_typescript)\n[03](https://stackblitz.com/github/dai-shi/react-tracked/tree/main/examples/03_usestate)\n[04](https://stackblitz.com/github/dai-shi/react-tracked/tree/main/examples/04_selector)\n[05](https://stackblitz.com/github/dai-shi/react-tracked/tree/main/examples/05_container)\n[06](https://stackblitz.com/github/dai-shi/react-tracked/tree/main/examples/06_customhook)\n[07](https://stackblitz.com/github/dai-shi/react-tracked/tree/main/examples/07_todolist)\n[08](https://stackblitz.com/github/dai-shi/react-tracked/tree/main/examples/08_comparison)\n[09](https://stackblitz.com/github/dai-shi/react-tracked/tree/main/examples/09_reactmemo)\n[10](https://stackblitz.com/github/dai-shi/react-tracked/tree/main/examples/10_untracked)\n[11](https://stackblitz.com/github/dai-shi/react-tracked/tree/main/examples/11_form)\n[12](https://stackblitz.com/github/dai-shi/react-tracked/tree/main/examples/12_async)\n[13](https://stackblitz.com/github/dai-shi/react-tracked/tree/main/examples/13_saga)\n\n## Benchmarks\n\nSee [this](https://github.com/dai-shi/react-tracked/issues/1#issuecomment-519509857) for details.\n\n## Blogs\n\n- [Super performant global state with React context and hooks](https://blog.axlight.com/posts/super-performant-global-state-with-react-context-and-hooks/)\n- [Redux-less context-based useSelector hook that has same performance as React-Redux](https://blog.axlight.com/posts/benchmark-react-tracked/)\n- [Four different approaches to non-Redux global state libraries](https://blog.axlight.com/posts/four-different-approaches-to-non-redux-global-state-libraries/)\n- [What is state usage tracking? A novel approach to intuitive and performant global state with React hooks and Proxy](https://blog.axlight.com/posts/what-is-state-usage-tracking-a-novel-approach-to-intuitive-and-performant-api-with-react-hooks-and-proxy/)\n- [How to use react-tracked: React hooks-oriented Todo List example](https://blog.axlight.com/posts/how-to-use-react-tracked-react-hooks-oriented-todo-list-example/)\n- [Effortless render optimization with state usage tracking with React hooks](https://blog.axlight.com/posts/effortless-render-optimization-with-state-usage-tracking-with-react-hooks/)\n- [4 options to prevent extra rerenders with React context](https://blog.axlight.com/posts/4-options-to-prevent-extra-rerenders-with-react-context/)\n- [React Tracked Documentation Website with Docusaurus v2](https://blog.axlight.com/posts/react-tracked-documentation-website-with-docusaurus-v2/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdai-shi%2Freact-tracked","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdai-shi%2Freact-tracked","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdai-shi%2Freact-tracked/lists"}