{"id":26243499,"url":"https://github.com/harryhope/hooked-on-redux","last_synced_at":"2025-10-06T07:54:21.151Z","repository":{"id":42884296,"uuid":"245698788","full_name":"harryhope/hooked-on-redux","owner":"harryhope","description":"🎣 ⚛️ Use redux through state hooks","archived":false,"fork":false,"pushed_at":"2025-03-10T03:33:11.000Z","size":4326,"stargazers_count":54,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-31T08:09:57.249Z","etag":null,"topics":["javascript","react","redux","updeep"],"latest_commit_sha":null,"homepage":"https://hooked-on-redux.js.org/","language":"JavaScript","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/harryhope.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2020-03-07T20:13:01.000Z","updated_at":"2025-03-10T03:32:51.000Z","dependencies_parsed_at":"2025-03-10T04:21:10.805Z","dependency_job_id":"e82a57c2-7798-4aeb-8ed5-8266c0894fdb","html_url":"https://github.com/harryhope/hooked-on-redux","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harryhope%2Fhooked-on-redux","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harryhope%2Fhooked-on-redux/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harryhope%2Fhooked-on-redux/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harryhope%2Fhooked-on-redux/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/harryhope","download_url":"https://codeload.github.com/harryhope/hooked-on-redux/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247648977,"owners_count":20972945,"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":["javascript","react","redux","updeep"],"created_at":"2025-03-13T10:28:31.770Z","updated_at":"2025-10-06T07:54:16.124Z","avatar_url":"https://github.com/harryhope.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![hooked-2x](https://user-images.githubusercontent.com/2415156/82620918-b531b980-9ba7-11ea-8d48-369160ff18b5.jpg)\n\n---\n\n![Tests](https://github.com/harryhope/hooked-on-redux/workflows/Tests/badge.svg?branch=master)\n\nHooked on Redux is a [React hook](https://reactjs.org/docs/hooks-intro.html) that lets you wield the power of [Redux](https://redux.js.org) with an interface that's as simple as the [`useState`](https://reactjs.org/docs/hooks-state.html) hook.\n\nRedux is a great way to manage React state and its core principle: [a single, immutable application state](https://redux.js.org/introduction/three-principles), allows for great features like [hot reloading and time travel debugging](https://www.youtube.com/watch?v=xsSnOQynTHs). Unfortunately, Redux is also intimidating to learn for beginners and requires a lot of [boilerplate](https://redux.js.org/recipes/reducing-boilerplate) that can slow down application development and add overhead to your codebase.\n\nHooked on Redux attempts to address these shortcomings by managing actions, selectors and reducers behind the scenes and exposing a simplified hook that lets you update Redux state in the same way you would update local component state with the `useState` hook.\n\n**It looks like this:**\n```jsx\nconst Example = props =\u003e {\n  const [count, setCount] = useHookedOnState('app.components.counterValue', 0)\n  return (\n    \u003cmain\u003e\n      \u003cp\u003eYou clicked {count} times.\u003c/p\u003e\n      \u003cbutton onClick={() =\u003e setCount(count + 1)}\u003eClick Me\u003c/button\u003e\n    \u003c/main\u003e\n  )\n}\n```\n\n## Getting Started\n\n### Installation\nHooked on Redux requires `react-redux` as a peer dependency. You'll also need `react`, `react-dom`, and `redux` if you don't have them installed already. This guide also assumes you are using npm and a module bundler like [webpack](https://webpack.js.org). To get started:\n```\nnpm i hooked-on-redux react-redux redux react-dom react\n```\n\n### Importing Dependencies\nHooked on Redux works with Redux and React, so we need to install all of the typical dependencies as well as `hooked-on-redux`.\n\nHooked on Redux can also work with your existing React/Redux app, so if you already have React and Redux installed you can skip importing from `react`, `react-dom`, `redux` and `react-redux`.\n\n```jsx\nimport React from 'react'\nimport ReactDOM from 'react-dom'\nimport {createStore} from 'redux'\nimport {Provider} from 'react-redux'\nimport {createHookedOnReducer, useHookedOnState} from 'hooked-on-redux'\n```\n\n### The Store\nHooked on Redux uses a Redux store (either a new or existing one) along with a reducer creator called `createHookedOnReducer`. This reducer creator will automatically manage state transformations so you won't need to think about reducers, actions or action creators.\n\n```jsx\nconst reducer = createHookedOnReducer()\nconst store = createStore(reducer, {})\n```\n\n### Provider\nHooked on Redux leverages the `\u003cProvider /\u003e` component from `react-redux`. If you've ever used Redux with React before you are probably already familiar with this step. In fact, most of this code should look identical to the [react-redux](https://react-redux.js.org/introduction/quick-start#provider) quick start guide.\n\n```jsx\nReactDOM.render(\n  \u003cProvider store={store}\u003e\n    \u003cCounter /\u003e\n  \u003c/Provider\u003e,\n  document.getElementById('app')\n)\n```\n\n### The Hook\nYou are now ready to start writing components using `useHookedOnState`.\n\n`useHookedOnState` is a [react hook](https://reactjs.org/docs/hooks-intro.html) that looks and functions similar to the [`useState`](https://reactjs.org/docs/hooks-state.html) hook. The only difference is it allows you to update a Redux store instead of local component state by specifying a slice of the global state to modify along with a default value in case nothing is there.\n\n```jsx\nconst Counter = props =\u003e {\n  const [count, setCount] = useHookedOnState('app.components.counterValue', 0)\n  return (\n    \u003cmain\u003e\n      \u003cp\u003eYou clicked {count} times.\u003c/p\u003e\n      \u003cbutton onClick={() =\u003e setCount(count + 1)}\u003eClick Me\u003c/button\u003e\n    \u003c/main\u003e\n  )\n}\n```\n\nNow, whenever you use the `setCount` function you defined, it will immutably update the Redux store to look like:\n```js\n{\n  app: {\n    components: {\n      counterValue: 1\n    }\n  }\n}\n```\n\nYou can update any slice of the global state this way by providing a path as the first parameter of `useHookedOnState`. The path string parameter works identically to [lodash's `_.set`](https://lodash.com/docs/4.17.15#set).\n\n## API\n\n### createHookedOnReducer\n```js\ncreateHookedOnReducer(initialState, namespace, handlers)\n```\n\nCreates a Redux reducer meant to be used with [`createStore`](https://redux.js.org/api/createstore#createstorereducer-preloadedstate-enhancer).\n\n**Arguments**\n\n`initialState`: _(any)_ The initial state of the slice of the Redux store controlled by the hooked-on-reducer. Use this parameter to define pre-existing state. **Default value:** `{}`\n\n`namespace`: _(string)_ Hooked on Redux generates Redux actions behind the scenes without you having to do anything. To allow interoperability with other actions, Hooked on Redux prefixes its own actions with a string. Anything with this string will be funneled through the hooked-on-reducer this function creates. **Default Value:** `'HOOKED_ON_REDUX'`\n\n`handlers`: _(object)_ If you need to add your own reducers to handle complicated state transforms or leverage existing reducers in your application, you can pass them through to `handlers`. For more information, see [Complex Actions](https://github.com/algolia/redux-updeep#complex-actions) in `redux-updeep` (which this library is built upon).\n\n```js\nexport default createHookedOnReducer(initialState, 'MY_NAMESPACE', {\n  'MY_NAMESPACE/COMPLEX_ACTION': (state, action) =\u003e {\n    return complexTransformation(state, action.payload)\n  }\n})\n```\n\n**Returns:** _(function)_ This function returns a function that can be used in Redux's `createStore`.\n\n---\n\n### useHookedOnState\n```js\nuseHookedOnState(selector, defaultState, options)\n```\n\n**Arguments**\n\n`selector`: _(string)_ Takes a path string similar to what you would use in [lodash's `_.set`](https://lodash.com/docs/4.17.15#set). This path specifies the \"slice\" of the store that you will be modifying.\n\n`defaultState`: _(any)_ This is the default value that will be used if the \"slice\" of the store specified by `selector` is empty. It works very similarly to [`useState`](https://reactjs.org/docs/hooks-state.html)'s default state.\n\n`options`: _(object)_ A configuration object that may contain the following properties:\n\n- `namespace`: _(string)_ If you are using a custom namespace for `createHookedOnReducer` then you must specify that namespace as the third parameter. If you are not using a default namespace then you can ignore this. **Default Value:** `'HOOKED_ON_REDUX'`\n\n- `rootPath`: _(string)_ If the Hooked on Redux reducer is not at the root level of your store, you must specify the subpath it exists on with this parameter. This usually happens if you integrate Hooked on Redux into a larger Redux codebase using something like `combineReducers`. For instance, if the Hooked on Redux reducer is added to the combined reducers with the name `myReducer`, then `rootPath` should be `myReducer` as well. For a nested path you may specify a path such as `path.to.my.store`.\n\n**Returns:** _(array)_ `[value, updateValue]` This function returns a \"tuple\" much like [`useState`](https://reactjs.org/docs/hooks-state.html). The first array element `value` is the value at the slice of state. The second element of the array `updateValue` is a function that accepts a single parameter that updates the global state at the slice of state specified by `selector`.\n\n## Prior Art\nHooked on Redux is inspired by (and used to leverage) [updeep](https://github.com/substantial/updeep) and [redux-updeep](https://github.com/algolia/redux-updeep). In particular, this article: [How we reduced boilerplate and handled asynchronous actions with redux](https://blog.algolia.com/how-we-reduced-boilerplate-and-handled-asynchronous-actions-with-redux/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharryhope%2Fhooked-on-redux","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fharryhope%2Fhooked-on-redux","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharryhope%2Fhooked-on-redux/lists"}