{"id":15726793,"url":"https://github.com/elderfo/use-dispatch-action","last_synced_at":"2025-03-31T01:48:48.170Z","repository":{"id":55004351,"uuid":"327172476","full_name":"elderfo/use-dispatch-action","owner":"elderfo","description":null,"archived":false,"fork":false,"pushed_at":"2021-01-19T03:31:01.000Z","size":485,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-18T15:46:46.862Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/elderfo.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}},"created_at":"2021-01-06T02:04:20.000Z","updated_at":"2021-01-16T00:29:25.000Z","dependencies_parsed_at":"2022-08-14T08:50:24.513Z","dependency_job_id":null,"html_url":"https://github.com/elderfo/use-dispatch-action","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elderfo%2Fuse-dispatch-action","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elderfo%2Fuse-dispatch-action/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elderfo%2Fuse-dispatch-action/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elderfo%2Fuse-dispatch-action/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elderfo","download_url":"https://codeload.github.com/elderfo/use-dispatch-action/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246403896,"owners_count":20771526,"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":[],"created_at":"2024-10-03T22:40:39.909Z","updated_at":"2025-03-31T01:48:48.110Z","avatar_url":"https://github.com/elderfo.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# use-dispatch-action\n\nTyped utilities for improving the experience with `useReducer`.\n\n![npm](https://img.shields.io/npm/v/use-dispatch-action)\n![GitHub Workflow Status](https://img.shields.io/github/workflow/status/elderfo/use-dispatch-action/CI)\n![node-current](https://img.shields.io/node/v/use-dispatch-action)\n![npm bundle size](https://img.shields.io/bundlephobia/min/use-dispatch-action)\n![NPM](https://img.shields.io/npm/l/use-dispatch-action)\n\n## Problem\n\nWhen using `useReducer`\n\n- Dispatching actions are not type safe\n- Action creators, while testable, introduce additional boilerplate code\n- Changing an action type can lead to a reducer to no longer work properly, if you don't have tests\n\n## Solution\n\n`use-dispatch-action` is a collection of utilities to improve the experience when using the `useReducer` hook.\n\n## Getting started\n\nInstall\n\n```bash\nyarn add use-dispatch-action\n```\n\nOr\n\n```bash\nnpm install use-dispatch-action\n```\n\n## Usage\n\n```typescript\nimport * as React from 'react';\nimport { useDispatchAction } from 'use-dispatch-action';\n\ntype Actions =\n  | { type: 'increment' }\n  | { type: 'decrement' }\n  | { type: 'addValue'; payload: number };\n\ntype State = { counter: number };\n\nconst reducer = (state: State, action: Actions): State =\u003e {\n  switch (action.type) {\n    case 'increment':\n      return { ...state, counter: state.counter + 1 };\n    case 'decrement':\n      return { ...state, counter: state.counter - 1 };\n    case 'addValue':\n      return { ...state, counter: state.counter + action.payload };\n    default:\n      return state;\n  }\n};\n\nconst Component = () =\u003e {\n  const [state, dispatch] = React.useReducer(reducer, { counter: 0 });\n  const increment = useDispatchAction(dispatch, 'increment');\n  const decrement = useDispatchAction(dispatch, 'decrement');\n  const addValue = useDispatchAction(dispatch, 'addValue');\n\n  return (\n    \u003cdiv\u003e\n      \u003cdiv title=\"counter\"\u003e{state.counter}\u003c/div\u003e\n      \u003cbutton onClick={increment}\u003eIncrement\u003c/button\u003e\n      \u003cbutton onClick={decrement}\u003eDecrement\u003c/button\u003e\n      \u003cbutton onClick={() =\u003e addValue(2)}\u003eAdd Two\u003c/button\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n# API\n\n## type `Action`\n\nA utilty type for defining actions\n\n```typescript\ntype Action\u003cTActionType extends string, TPayload = never\u003e = {\n  type: TActionType;\n  payload?: TPayload;\n};\n```\n\n### Example\n\n```typescript\ntype Actions = Action\u003c'incrementOne'\u003e | Action\u003c'increment', number\u003e;\n```\n\n## `useDispatchAction`\n\nCreates type safe dispatch functions for the specified action\n\n```typescript\nuseDispatchAction\u003cTAction\u003e(\n    dispatch: React.Dispatch\u003cTAction\u003e,\n    action: string\n) : DispatchAction\u003cTActionPayload\u003e\n```\n\n### Arguments\n\n- `dispatch: React.Dispatch\u003cTAction\u003e` - A dispatch method retured from `React.useReducer`\n- `action: string` - The type of the action\n\n### Returns\n\n- `DispatchAction\u003cTActionPayload\u003e` - Function to dispatch action\n\n```typescript\n// For actions without a payload\n() =\u003e void;\n// For actions with a payload\n(payload: TPayload) =\u003e void;\n```\n\n### Example ([types/reducer](#types-and-reducer-for-examples))\n\n```typescript\nconst Component = () =\u003e {\n  const [state, dispatch] = React.useReducer(reducer, { counter: 0 });\n  const increment = useDispatchAction(dispatch, 'increment');\n  const decrement = useDispatchAction(dispatch, 'decrement');\n  const addValue = useDispatchAction(dispatch, 'addValue');\n\n  return (\n    \u003cdiv\u003e\n      \u003cdiv title=\"counter\"\u003e{state.counter}\u003c/div\u003e\n      \u003cbutton onClick={() =\u003e increment()}\u003eIncrement\u003c/button\u003e\n      \u003cbutton onClick={() =\u003e decrement()}\u003eDecrement\u003c/button\u003e\n      \u003cbutton onClick={() =\u003e addValue(2)}\u003eAdd Two\u003c/button\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n## `useDispatchReducer`\n\nCreates a reducer with a type safe dispatch method\n\n```typescript\nuseDispatchReducer\u003cTState, TAction\u003e (\n   reducer: React.Reducer\u003cTState, TAction\u003e,\n   initialState: TState\n) : [state: TState, ActionDispatcher\u003cTAction\u003e]\n```\n\n`TState` and `TAction` can be infered by providing the type of a reducer.\n\n```typescript\nuseDispatchReducer\u003cTReducer\u003e(\n   reducer: TReducer,\n   initialState: TState\n) : [state: TState, ActionDispatcher\u003cTAction\u003e]\n```\n\n### Arguments\n\n- `reducer: React.Reducer\u003cTState, TAction\u003e` - The reducer\n- `initialState: TState` - State to initialize the reducer with. A note, `useDispatchReducer` does not implement lazy loading the state\n\n### Returns\n\nA tuple with:\n\n- `state: TState` - State of the reducer\n- `ActionDispatcher\u003cTAction\u003e` - Function to dispatch actions in the form of tuples\n  ```typescript\n  // For actions without a payload\n  ([type: string]) =\u003e void;\n  // For actions with a payload\n  ([type: string, payload: TPayload]) =\u003e void;\n  ```\n\n### Examples ([types/reducer](#types-and-reducer-for-examples))\n\nWith type inference\n\n```typescript\nconst Component = () =\u003e {\n  const [state, dispatch] = useDispatchReducer(reducer, { counter: 0 });\n  const increment = () =\u003e dispatch(['increment']);\n  const decrement = () =\u003e dispatch(['decrement']);\n  const addValue = (number: number) =\u003e dispatch(['addValue', number]);\n\n  return (\n    \u003cdiv\u003e\n      \u003cdiv title=\"counter\"\u003e{state.counter}\u003c/div\u003e\n      \u003cbutton onClick={increment}\u003eIncrement\u003c/button\u003e\n      \u003cbutton onClick={decrement}\u003eDecrement\u003c/button\u003e\n      \u003cbutton onClick={() =\u003e addValue(2)}\u003eAdd Two\u003c/button\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\nWith known State and Action types\n\n```typescript\nconst Component = () =\u003e {\n  const [state, dispatch] = useDispatchReducer\u003cState, Action\u003e(reducer, {\n    counter: 0,\n  });\n  const increment = () =\u003e dispatch(['increment']);\n  const decrement = () =\u003e dispatch(['decrement']);\n  const addValue = (number: number) =\u003e dispatch(['addValue', number]);\n\n  return (\n    \u003cdiv\u003e\n      \u003cdiv title=\"counter\"\u003e{state.counter}\u003c/div\u003e\n      \u003cbutton onClick={increment}\u003eIncrement\u003c/button\u003e\n      \u003cbutton onClick={decrement}\u003eDecrement\u003c/button\u003e\n      \u003cbutton onClick={() =\u003e addValue(2)}\u003eAdd Two\u003c/button\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\nOnly know the State type? The Action type can be inferred from the reducer as long as the actions are typed.\n\n```typescript\nconst Component = () =\u003e {\n  const [state, dispatch] = useDispatchReducer\u003cState\u003e(reducer, { counter: 0 });\n  const increment = () =\u003e dispatch(['increment']);\n  const decrement = () =\u003e dispatch(['decrement']);\n  const addValue = (number: number) =\u003e dispatch(['addValue', number]);\n\n  return (\n    \u003cdiv\u003e\n      \u003cdiv title=\"counter\"\u003e{state.counter}\u003c/div\u003e\n      \u003cbutton onClick={increment}\u003eIncrement\u003c/button\u003e\n      \u003cbutton onClick={decrement}\u003eDecrement\u003c/button\u003e\n      \u003cbutton onClick={() =\u003e addValue(2)}\u003eAdd Two\u003c/button\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n## DispatchContext\n\nA context based dispatcher used to prevent prop drilling\n\n```typescript\nexport type DispatchContextProps = {\n  initialState: TState;\n  reducer: React.Reducer\u003cTState, TAction\u003e;\n};\n```\n\n### props\n\n- `initialState: TState` - state to initialize the reducer with\n- `reducer: React.Reducer\u003cTState, TAction\u003e` - The reducer\n\n### Examples ([types/reducer](#types-and-reducer-for-examples))\n\nUsing a consumer\n\n```typescript\nconst DispatchContext = () =\u003e {\n  return (\n    \u003cDispatchContextProvider reducer={reducer} initialState={{ counter: 0 }}\u003e\n      \u003cDispatchContextConsumer\u003e\n        {({ state, dispatch }: DispatchProps\u003ctypeof reducer\u003e) =\u003e (\n          \u003cdiv\u003e\n            \u003cdiv title=\"counter\"\u003e{state.counter}\u003c/div\u003e\n            \u003cbutton onClick={() =\u003e dispatch(['increment'])}\u003eIncrement\u003c/button\u003e\n            \u003cbutton onClick={() =\u003e dispatch(['decrement'])}\u003eDecrement\u003c/button\u003e\n            \u003cbutton onClick={() =\u003e dispatch(['addValue', 2])}\u003eAdd Two\u003c/button\u003e\n          \u003c/div\u003e\n        )}\n      \u003c/DispatchContextConsumer\u003e\n    \u003c/DispatchContextProvider\u003e\n  );\n};\n```\n\nUsing `useDispatchContext`\n\n```typescript\nconst Component = () =\u003e {\n  return (\n    \u003cDispatchContextProvider initialState={{ counter: 0 }} reducer={reducer}\u003e\n      \u003cCounter /\u003e\n    \u003c/DispatchContextProvider\u003e\n  );\n};\n\nconst Counter = () =\u003e {\n  const [state, dispatch] = useDispatchContext\u003ctypeof reducer\u003e();\n\n  return (\n    \u003cdiv\u003e\n      \u003cdiv title=\"counter\"\u003e{state.counter}\u003c/div\u003e\n      \u003cbutton onClick={() =\u003e dispatch(['increment'])}\u003eIncrement\u003c/button\u003e\n      \u003cbutton onClick={() =\u003e dispatch(['decrement'])}\u003eDecrement\u003c/button\u003e\n      \u003cbutton onClick={() =\u003e dispatch(['addValue', 2])}\u003eAdd Two\u003c/button\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n## Types and reducer for examples\n\n```typescript\ntype Actions =\n  | { type: 'increment' }\n  | { type: 'decrement' }\n  | { type: 'addValue'; payload: number };\n\ntype State = { counter: number };\n\nconst reducer = (state: State, action: Actions): State =\u003e {\n  switch (action.type) {\n    case 'increment':\n      return { ...state, counter: state.counter + 1 };\n    case 'decrement':\n      return { ...state, counter: state.counter - 1 };\n    case 'addValue':\n      return { ...state, counter: state.counter + action.payload };\n    default:\n      return state;\n  }\n};\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felderfo%2Fuse-dispatch-action","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felderfo%2Fuse-dispatch-action","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felderfo%2Fuse-dispatch-action/lists"}