{"id":13450331,"url":"https://github.com/dai-shi/reactive-react-redux","last_synced_at":"2025-04-13T00:47:58.344Z","repository":{"id":38185236,"uuid":"157671629","full_name":"dai-shi/reactive-react-redux","owner":"dai-shi","description":"[NOT MAINTAINED] React Redux binding with React Hooks and Proxy","archived":false,"fork":false,"pushed_at":"2024-01-12T06:32:14.000Z","size":7249,"stargazers_count":504,"open_issues_count":27,"forks_count":11,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-04-13T00:47:43.503Z","etag":null,"topics":["hooks-api","proxy","react","react-hooks","react-redux","reactive","reactjs","redux"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/reactive-react-redux","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":null,"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}},"created_at":"2018-11-15T07:41:19.000Z","updated_at":"2024-12-18T23:19:44.000Z","dependencies_parsed_at":"2024-06-21T05:44:25.896Z","dependency_job_id":"0787328e-100b-42f5-a16b-4288897775fe","html_url":"https://github.com/dai-shi/reactive-react-redux","commit_stats":{"total_commits":305,"total_committers":4,"mean_commits":76.25,"dds":"0.049180327868852514","last_synced_commit":"868a2b267751a518f6e496172e1b3126bdeb63b3"},"previous_names":["dai-shi/react-hooks-easy-redux"],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dai-shi%2Freactive-react-redux","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dai-shi%2Freactive-react-redux/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dai-shi%2Freactive-react-redux/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dai-shi%2Freactive-react-redux/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dai-shi","download_url":"https://codeload.github.com/dai-shi/reactive-react-redux/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248650435,"owners_count":21139672,"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":["hooks-api","proxy","react","react-hooks","react-redux","reactive","reactjs","redux"],"created_at":"2024-07-31T07:00:33.685Z","updated_at":"2025-04-13T00:47:58.321Z","avatar_url":"https://github.com/dai-shi.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","Packages"],"sub_categories":[],"readme":"This project is no longer maintained.\n[react-tracked](https://react-tracked.js.org) works with react-redux\nand covers the use case of reactive-react-redux.\nRedux docs officially recommends [proxy-memoize](https://redux.js.org/usage/deriving-data-selectors#proxy-memoize) as a selector library,\nand it provides similar developer experience to that of reactive-react-redux.\nBoth are good options.\n\n---\n\nThere are several projects related to this repo.\nHere's the index of those.\n\n- reactive-react-redux v5-alpha (this repo): This has an experimental react-redux binding with useMutableSource. It provides useTrackedState, which tracks the usage of state in render, and it's originally proposed in this repo.\n- [react-tracked](https://github.com/dai-shi/react-tracked): This project is to provide useTrackedState with React Context. v1.6 provides createTrackedSelector that will create useTrackedState from useSelector.\n- [react-redux #1503](https://github.com/reduxjs/react-redux/pull/1503): A pull request to add useTrackedState to the official react-redux library.\n- [proxy-memoize](https://github.com/dai-shi/proxy-memoize): This is another project which is not tied to React, but combined with useSelector, we get a similar functionality like useTrackedState.\n\n---\n\n# reactive-react-redux\n\n[![CI](https://img.shields.io/github/workflow/status/dai-shi/reactive-react-redux/CI)](https://github.com/dai-shi/reactive-react-redux/actions?query=workflow%3ACI)\n[![npm](https://img.shields.io/npm/v/reactive-react-redux)](https://www.npmjs.com/package/reactive-react-redux)\n[![size](https://img.shields.io/bundlephobia/minzip/reactive-react-redux)](https://bundlephobia.com/result?p=reactive-react-redux)\n[![discord](https://img.shields.io/discord/627656437971288081)](https://discord.gg/MrQdmzd)\n\nReact Redux binding with React Hooks and Proxy\n\n\u003e If you are looking for a non-Redux library, please visit [react-tracked](https://github.com/dai-shi/react-tracked) which has the same hooks API.\n\n## Introduction\n\nThis is a library to bind React and Redux with Hooks API.\nIt has mostly the same API as the official\n[react-redux Hooks API](https://react-redux.js.org/api/hooks),\nso it can be used as a drop-in replacement\nif you are using only basic functionality.\n\nThere are two major features in this library\nthat are not in the official react-redux.\n\n### 1. useTrackedState hook\n\nThis library provides another hook `useTrackedState`\nwhich is a simpler API than already simple `useSelector`.\nIt returns an entire state, but the library takes care of\noptimization of re-renders.\nMost likely, `useTrackedState` performs better than\n`useSelector` without perfectly tuned selectors.\n\nTechnically, `useTrackedState` has no [stale props](https://react-redux.js.org/api/hooks#stale-props-and-zombie-children) issue.\n\n### 2. useMutableSource without Context\n\nreact-redux v7 has APIs around Context.\nThis library is implemented with useMutableSource,\nand it patches the Redux store.\nAPIs are provided without Context.\nIt's up to developers to use Context based on them.\nCheck out `./examples/11_todolist/src/context.ts`.\n\nThere's another difference from react-redux v7.\nThis library directly use useMutableSource, and requires\nuseCallback for the selector in useSelector.\n[equalityFn](https://react-redux.js.org/api/hooks#equality-comparisons-and-updates) is not supported.\n\n## How tracking works\n\nA hook `useTrackedState` returns an entire Redux state object with Proxy,\nand it keeps track of which properties of the object are used\nin render. When the state is updated, this hook checks\nwhether used properties are changed.\nOnly if it detects changes in the state,\nit triggers a component to re-render.\n\n## Install\n\n```bash\nnpm install reactive-react-redux\n```\n\n## Usage (useTrackedState)\n\n```javascript\nimport React from 'react';\nimport { createStore } from 'redux';\nimport {\n  patchStore,\n  useTrackedState,\n} from 'reactive-react-redux';\n\nconst initialState = {\n  count: 0,\n  text: 'hello',\n};\n\nconst reducer = (state = initialState, action) =\u003e {\n  switch (action.type) {\n    case 'increment': return { ...state, count: state.count + 1 };\n    case 'decrement': return { ...state, count: state.count - 1 };\n    case 'setText': return { ...state, text: action.text };\n    default: return state;\n  }\n};\n\nconst store = patchStore(createStore(reducer));\n\nconst Counter = () =\u003e {\n  const state = useTrackedState(store);\n  const { dispatch } = store;\n  return (\n    \u003cdiv\u003e\n      {Math.random()}\n      \u003cdiv\u003e\n        \u003cspan\u003eCount: {state.count}\u003c/span\u003e\n        \u003cbutton type=\"button\" onClick={() =\u003e dispatch({ type: 'increment' })}\u003e+1\u003c/button\u003e\n        \u003cbutton type=\"button\" onClick={() =\u003e dispatch({ type: 'decrement' })}\u003e-1\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\n\nconst TextBox = () =\u003e {\n  const state = useTrackedState(store);\n  const { dispatch } = store;\n  return (\n    \u003cdiv\u003e\n      {Math.random()}\n      \u003cdiv\u003e\n        \u003cspan\u003eText: {state.text}\u003c/span\u003e\n        \u003cinput value={state.text} onChange={event =\u003e dispatch({ type: 'setText', text: event.target.value })} /\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\n\nconst App = () =\u003e (\n  \u003c\u003e\n    \u003ch1\u003eCounter\u003c/h1\u003e\n    \u003cCounter /\u003e\n    \u003cCounter /\u003e\n    \u003ch1\u003eTextBox\u003c/h1\u003e\n    \u003cTextBox /\u003e\n    \u003cTextBox /\u003e\n  \u003c/\u003e\n);\n```\n\n## API\n\n\u003c!-- Generated by documentation.js. Update this documentation by updating the source code. --\u003e\n\n### patchStore\n\npatch Redux store for React\n\n#### Parameters\n\n-   `store` **Store\u0026lt;State, Action\u003e** \n\n#### Examples\n\n```javascript\nimport { createStore } from 'redux';\nimport { patchStore } from 'reactive-react-redux';\n\nconst reducer = ...;\nconst store = patchStore(createStore(reducer));\n```\n\n### useTrackedState\n\nuseTrackedState hook\n\nIt return the Redux state wrapped by Proxy,\nand the state prperty access is tracked.\nIt will only re-render if accessed properties are changed.\n\n#### Parameters\n\n-   `patchedStore` **PatchedStore\u0026lt;State, Action\u003e** \n-   `opts` **Opts**  (optional, default `{}`)\n\n#### Examples\n\n```javascript\nimport { useTrackedState } from 'reactive-react-redux';\n\nconst Component = () =\u003e {\n  const state = useTrackedState(store);\n  ...\n};\n```\n\n### useSelector\n\nuseSelector hook\n\nselector has to be stable. Either define it outside render\nor use useCallback if selector uses props.\n\n#### Parameters\n\n-   `patchedStore` **PatchedStore\u0026lt;State, Action\u003e** \n-   `selector` **function (state: State): Selected** \n\n#### Examples\n\n```javascript\nimport { useCallback } from 'react';\nimport { useSelector } from 'reactive-react-redux';\n\nconst Component = ({ count }) =\u003e {\n  const isBigger = useSelector(store, useCallack(state =\u003e state.count \u003e count, [count]));\n  ...\n};\n```\n\n### memo\n\nmemo\n\nUsing `React.memo` with tracked state is not compatible,\nbecause `React.memo` stops state access, thus no tracking occurs.\nThis is a special memo to be used instead of `React.memo` with tracking support.\n\n#### Parameters\n\n-   `Component` **any** \n-   `areEqual` **any?** \n\n#### Examples\n\n```javascript\nimport { memo } from 'reactive-react-redux';\n\nconst ChildComponent = memo(({ obj1, obj2 }) =\u003e {\n  // ...\n});\n```\n\n## Recipes\n\n### Context\n\nYou can create Context based APIs like react-redux v7.\n\n```typescript\nimport { createContext, createElement, useContext } from 'react';\nimport {\n  PatchedStore,\n  useSelector as useSelectorOrig,\n  useTrackedState as useTrackedStateOrig,\n} from 'reactive-react-redux';\n\nexport type State = ...;\n\nexport type Action = ...;\n\nconst Context = createContext(new Proxy({}, {\n  get() { throw new Error('use Provider'); },\n}) as PatchedStore\u003cState, Action\u003e);\n\nexport const Provider: React.FC\u003c{ store: PatchedStore\u003cState, Action\u003e }\u003e = ({\n  store,\n  children,\n}) =\u003e createElement(Context.Provider, { value: store }, children);\n\nexport const useDispatch = () =\u003e useContext(Context).dispatch;\n\nexport const useSelector = \u003cSelected\u003e(\n  selector: (state: State) =\u003e Selected,\n) =\u003e useSelectorOrig(useContext(Context), selector);\n\nexport const useTrackedState = () =\u003e useTrackedStateOrig(useContext(Context));\n```\n\n### useTrackedSelector\n\nYou can create a selector hook with tracking support.\n\n```javascript\nimport { useTrackedState } from 'reactive-react-redux';\n\nexport const useTrackedSelector = (patchedStore, selector) =\u003e selector(useTrackedState(patchedStore));\n```\n\nPlease refer [this issue](https://github.com/dai-shi/reactive-react-redux/issues/41) for more information.\n\n### useTracked\n\nYou can combine useTrackedState and useDispatch to\nmake a hook that returns a tuple like `useReducer`.\n\n```javascript\nimport { useTrackedState, useDispatch } from 'reactive-react-redux';\n\nexport const useTracked = (patchedStore) =\u003e {\n  const state = useTrackedState(patchedStore);\n  const dispatch = useDispatch(patchedStore);\n  return useMemo(() =\u003e [state, dispatch], [state, dispatch]);\n};\n```\n\n## Caveats\n\nProxy and state usage tracking may not work 100% as expected.\nThere are some limitations and workarounds.\n\n### Proxied states are referentially equal only in per-hook basis\n\n```javascript\nconst state1 = useTrackedState(patchedStore);\nconst state2 = useTrackedState(patchedStore);\n// state1 and state2 is not referentially equal\n// even if the underlying redux state is referentially equal.\n```\n\nYou should use `useTrackedState` only once in a component.\n\n### An object referential change doesn't trigger re-render if an property of the object is accessed in previous render\n\n```javascript\nconst state = useTrackedState(patchedStore);\nconst { foo } = state;\nreturn \u003cChild key={foo.id} foo={foo} /\u003e;\n\nconst Child = React.memo(({ foo }) =\u003e {\n  // ...\n};\n// if foo doesn't change, Child won't render, so foo.id is only marked as used.\n// it won't trigger Child to re-render even if foo is changed.\n```\n\nYou need to use a special `memo` provided by this library.\n\n```javascript\nimport { memo } from 'reactive-react-redux';\n\nconst Child = memo(({ foo }) =\u003e {\n  // ...\n};\n```\n\n### Proxied state might behave unexpectedly outside render\n\nProxies are basically transparent, and it should behave like normal objects.\nHowever, there can be edge cases where it behaves unexpectedly.\nFor example, if you console.log a proxied value,\nit will display a proxy wrapping an object.\nNotice, it will be kept tracking outside render,\nso any prorerty access will mark as used to trigger re-render on updates.\n\nuseTrackedState will unwrap a Proxy before wrapping with a new Proxy,\nhence, it will work fine in usual use cases.\nThere's only one known pitfall: If you wrap proxied state with your own Proxy\noutside the control of useTrackedState,\nit might lead memory leaks, because useTrackedState\nwouldn't know how to unwrap your own Proxy.\n\nTo work around such edge cases, the first option is to use primitive values.\n\n```javascript\nconst state = useTrackedState(patchedStore);\nconst dispatch = useUpdate(patchedStore);\ndispatch({ type: 'FOO', value: state.fooObj }); // Instead of using objects,\ndispatch({ type: 'FOO', value: state.fooStr }); // Use primitives.\n```\n\nThe second option is to use `getUntrackedObject`.\n\n```javascript\nimport { getUntrackedObject } from 'react-tracked';\ndispatch({ type: 'FOO', value: getUntrackedObject(state.fooObj) });\n```\n\nYou could implement a special dispatch function to do this automatically.\n\n## Examples\n\nThe [examples](examples) folder contains working examples.\nYou can run one of them with\n\n```bash\nPORT=8080 npm run examples:01_minimal\n```\n\nand open \u003chttp://localhost:8080\u003e in your web browser.\n\nYou can also try them in codesandbox.io:\n[01](https://codesandbox.io/s/github/dai-shi/reactive-react-redux/tree/master/examples/01_minimal)\n[02](https://codesandbox.io/s/github/dai-shi/reactive-react-redux/tree/master/examples/02_typescript)\n[03](https://codesandbox.io/s/github/dai-shi/reactive-react-redux/tree/master/examples/03_deep)\n[04](https://codesandbox.io/s/github/dai-shi/reactive-react-redux/tree/master/examples/04_immer)\n[05](https://codesandbox.io/s/github/dai-shi/reactive-react-redux/tree/master/examples/05_localstate)\n[06](https://codesandbox.io/s/github/dai-shi/reactive-react-redux/tree/master/examples/06_memoization)\n[07](https://codesandbox.io/s/github/dai-shi/reactive-react-redux/tree/master/examples/07_multistore)\n[08](https://codesandbox.io/s/github/dai-shi/reactive-react-redux/tree/master/examples/08_dynamic)\n[09](https://codesandbox.io/s/github/dai-shi/reactive-react-redux/tree/master/examples/09_thunk)\n[11](https://codesandbox.io/s/github/dai-shi/reactive-react-redux/tree/master/examples/11_todolist)\n[12](https://codesandbox.io/s/github/dai-shi/reactive-react-redux/tree/master/examples/12_async)\n[13](https://codesandbox.io/s/github/dai-shi/reactive-react-redux/tree/master/examples/13_memo)\n\n## Benchmarks\n\n\u003cimg alt=\"benchmark result\" src=\"https://user-images.githubusercontent.com/490574/61585382-405fae80-ab95-11e9-9f28-3b1a49dd1e5f.png\" width=\"425\" /\u003e\n\nSee [#32](https://github.com/dai-shi/reactive-react-redux/issues/32) for details.\n\n## Blogs\n\n-   [A deadly simple React bindings library for Redux with Hooks API](https://blog.axlight.com/posts/a-deadly-simple-react-bindings-library-for-redux-with-hooks-api/)\n-   [Developing React custom hooks for Redux without react-redux](https://blog.axlight.com/posts/developing-react-custom-hooks-for-redux-without-react-redux/)\n-   [Integrating React and Redux, with Hooks and Proxies](https://frontarm.com/daishi-kato/redux-custom-hooks/)\n-   [New React Redux coding style with hooks without selectors](https://blog.axlight.com/posts/new-react-redux-coding-style-with-hooks-without-selectors/)\n-   [Benchmark alpha-released hooks API in React Redux with alternatives](https://blog.axlight.com/posts/benchmark-alpha-released-hooks-api-in-react-redux-with-alternatives/)\n-   [Four patterns for global state with React hooks: Context or Redux](https://blog.axlight.com/posts/four-patterns-for-global-state-with-react-hooks-context-or-redux/)\n-   [Redux meets hooks for non-redux users: a small concrete example with reactive-react-redux](https://blog.axlight.com/posts/redux-meets-hooks-for-non-redux-users-a-small-concrete-example-with-reactive-react-redux/)\n-   [Redux-less context-based useSelector hook that has same performance as React-Redux](https://blog.axlight.com/posts/benchmark-react-tracked/)\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-   [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-   [How I developed a Concurrent Mode friendly library for React Redux](https://blog.axlight.com/posts/how-i-developed-a-concurrent-mode-friendly-library-for-react-redux/)\n-   [React hooks-oriented Redux coding pattern without thunks and action creators](https://blog.axlight.com/posts/react-hooks-oriented-redux-coding-pattern-without-thunks-and-action-creators/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdai-shi%2Freactive-react-redux","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdai-shi%2Freactive-react-redux","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdai-shi%2Freactive-react-redux/lists"}