{"id":19636558,"url":"https://github.com/mizchi/hard-reducer","last_synced_at":"2025-04-28T08:32:45.016Z","repository":{"id":57261352,"uuid":"118875570","full_name":"mizchi/hard-reducer","owner":"mizchi","description":"Type friendly reducer helper","archived":false,"fork":false,"pushed_at":"2020-06-02T23:47:34.000Z","size":1300,"stargazers_count":56,"open_issues_count":11,"forks_count":6,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-04-25T22:40:46.280Z","etag":null,"topics":["flowtype","fsa","reducer","redux","typescript"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mizchi.png","metadata":{"files":{"readme":"README.md","changelog":"ChangeLog.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-01-25T07:00:08.000Z","updated_at":"2021-05-07T15:58:38.000Z","dependencies_parsed_at":"2022-08-25T06:01:42.604Z","dependency_job_id":null,"html_url":"https://github.com/mizchi/hard-reducer","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mizchi%2Fhard-reducer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mizchi%2Fhard-reducer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mizchi%2Fhard-reducer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mizchi%2Fhard-reducer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mizchi","download_url":"https://codeload.github.com/mizchi/hard-reducer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224103117,"owners_count":17256306,"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":["flowtype","fsa","reducer","redux","typescript"],"created_at":"2024-11-11T12:29:49.701Z","updated_at":"2024-11-11T12:29:50.510Z","avatar_url":"https://github.com/mizchi.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# hard-reducer\n\n[![CircleCI](https://circleci.com/gh/mizchi/hard-reducer.svg?style=svg)](https://circleci.com/gh/mizchi/hard-reducer) [![Greenkeeper badge](https://badges.greenkeeper.io/mizchi/hard-reducer.svg)](https://greenkeeper.io/)\n\nType friendly facade for better reducer.\n\n```sh\nnpm install hard-reducer --save\n# or\nyarn add hard-reducer\n```\n\n## Concepts\n\n- Type safe interface\n- Avoid redundant `type` string definitions\n- Keep reducer interface `(State, Action) =\u003e State` to use with `redux.combineReducers()`\n- Handle Flux Standard Action `\u003cPayload\u003e{ type: string, payload: Payload }`\n\nCheck this code to know detail.\n\n[Flow playground](https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoUYCmBnGBLAOwBcBaAEz2wEMAjGTZNIgTwAcGBBAYyLzgIDCAJ0xUicIQB4B-bESFVCRAFxg5QwgHMANGACSBVgFciugApVm8KmQB8YALxgA3qjBgAFAeNEAlKucwFnZVGQJ1RWJdVktrMlULKzgbMABfVHTUYIYAJUwyIy5MKQBlIjFMeydXdw8yit0qAmZ-MHqiTG03ME1MFU9fR3s8gqLS8o7bLvcuKmxMaVl5SNN9QxNzWOS7D273bl5+YVFxKTCIpV1vDbBEuKm9z3bO262bQYd7Z+7WkcLiyTPWwZVCoKBGAg8PgEMA0Ix4GBkA7Q45iCQeOCsIjYAJgVgiKB4AAeAH5VOotGlBjUwFwlniCcTHGBMdiAHT4zCEolgAA+vLAAHJBaD3ODIYcYVwRBVkfxFuFlkpyfItFd1qs7ttbLt3O5+uclVFHlACKprkQhq8kjYfqo5YIZacFRcomsfJsbXYXI86YqgmwGE5OdywABqIK++lQagAMTNYCazGZHkIPnNGo+9hperAIiIRiEMJzuYD7GmpfcMS9qlNYAAZPWwKbU5nHu50rnO3qY1R42yAPqWpzZR75wsw3vx7qZMUQqH8WlOzBmIRwAC2lEwDpdRv6FIIOndNy1Nh1jwNSwUlxNCYtVtXG63klP3oFr7tYAdqOdhuvbotT17h9XM-TkMsgwZLkmQjIgo39KcEyTFM0xMDMfCzEDS3HIssMrbIK0ras4gSNdN3mNkRGwOAYAAN0wDwW1QvxfHbNJHm7MU4wIQdhwgsc+gnZtuJnUVm3nSUlxOTASjwddWHoHc-xWFUNEPc9c0vRV-zg9xWm-J0JF3HTdFouA8DIUzzO9EswMtbJmRDGDI1A6NuNUZCnA8TCPECBzUlY3NEN45lR1zHDJxEjsxIilwpNlBcCF0aVpNk+TFMS5Ll0fcjt0S9jMnFfKUoqP4xkBCZKlbPBeCoGBnlUZ5fnyf5xgqbNunoS0oBETAAC98mZKA6vmbo7LAdcqFYZkCEwJAAFkpu8sTxpEUZig85oUzkCpNuTJxCBqvA6ueRpEr2zDbPpAALJoyHoIRmUm1g2V6IgPCoRK2WyQK9Vi26CHu4owBJMAAaBoQPB2jozslDk3jIQYVQqUTujW1q2VmeYU0+yVY3nZK6pgGhPoAa0ux5nrZeZ3tx6F8chXjCZgYmyd+9xYvRsZRI5wTcK54oQXQYAwCMahelBcaaRKjoHQrGWZLkhS8sleXsrIrcHQyZk4QRJFEp-dEaScolVEFOkIQ6IRgBFALJfpKi+mZBW0uVh0PEFR2iEFVjxsILhneXd3BX9wVdA8Wi6tUAgjHXGhikwyOYF9+kyEwAOnAV4O064MPPCT6PY-joRE7qlP-X9jhsGYSFA+knLNcSj3-ZIOYa9z8OC7AGO44Tq0aVihuKKomj6IjsuMlYrJAzaSrmUCJOjEwQve8e9I-YII6Trn6owEX5ewAABnY1a69Klqxmq2r6sq37MbmBj-fD6GXmI7YKdc-1WFUylgwRsBgAi0PAJAsuESzuH3sjDobJ97hjxAjDiM475YwYjnZ+lVogIw-n9PmxY2KQLUJVGBdUl5gBIPAr0iCOy-UAXmTAABHeEIg6EFCJCQfET55gkE3GQIGCAqAiG6PfeYrYuBV3bughoFC4jYJmA7PoK9i6OQQZ-cC+YABMP9DzKK9AAoBmgQFCUOtfb41ChEoI8F7SRMNpHv37vBNRCju5F2Bn-XRtDgHhVwWAYxx0b4ozMagVafRj5OEhgvEhB9j6pF0P7DwABmXw5dHFEAAIzMkhvmQ+ugvbeWSZaDRGTLF9FSboHOHh1FJKCbIGimA2TwE0MUoglTUBAA)\n\n## Examples\n\nThis code is runnable in both flowtype and typescript\n\n```js\n/* @flow */\n// ^ flow magic comment to activate. It will be ignored in typescript.\nimport {\n  buildActionCreator,\n  createReducer,\n  type ActionCreator\n} from \"hard-reducer\";\n// ^ If you use typescript, Do not use `type` before ActionCreator\nconst { createAction } = buildActionCreator({ prefix: \"counter/\" });\n\n// Add type to your payload by ActionCreator\nconst inc: ActionCreator\u003cnumber\u003e = createAction(\"inc\");\n// or infer by function result\nconst dec = createAction(\"dec\", (val: number) =\u003e val);\n\ninc(1); //=\u003e { type: 'counter/inc', payload: 1 }\n\n// Define state type\ntype State = { value: number };\nconst initialState: State = { value: 0 };\n\nconst reducer = createReducer(initialState)\n  // Handle `(State, Payload) =\u003e State` in matched context.\n  .case(inc, (state, payload) =\u003e {\n    return {\n      value: state.value + payload\n    };\n  })\n  .case(dec, (state, payload) =\u003e {\n    // $ExpectError\n    const p: string = payload;\n    return {\n      value: state.value - payload\n    };\n  })\n  // Take string\n  .case(\"other/noop\", (state, payload) =\u003e {\n    return state;\n  })\n  // Take all uncaught action, not payload!\n  .else((state, action) =\u003e {\n    console.log(\"default fallback\");\n    return state;\n  });\n\n// Use it\nconst ret0 = reducer(initialState, inc(3));\nconst ret1 = reducer(ret1, dec(1));\n```\n\nSee detail in `index.js.flow` or `index.d.ts`\n\n### Handle async action: createAsyncAction\n\n`createAsyncAction(...)` returns `{ resolved, rejected, started }` and callable method.\n\n(You need to add `redux-thunk` in store's middlewares)\n\n```js\n/* @flow */\nimport { createReducer, buildActionCreator } from \"hard-reducer\";\nconst { createAsyncAction } = buildActionCreator();\n\nconst incAsync = createAsyncAction(\"inc-async\", async (val: number) =\u003e {\n  if (val % 2 === 1) {\n    throw new Error(\"error\");\n  }\n  return {\n    p: 1\n  };\n});\n\ntype Status = \"ready\" | \"started\" | \"resolved\" | \"rejected\";\ntype State = { status: Status, payload: ?{ p: number } };\n\nconst reducer = createReducer({ status: \"ready\", payload: null })\n  .case(incAsync.started, state =\u003e {\n    return { state: \"started\" };\n  })\n  .case(incAsync.resolved, (state, payload) =\u003e {\n    return { state: \"resolve\", payload };\n  })\n  .case(incAsync.rejected, (state, error) =\u003e {\n    return { state: \"ready\", payload: null };\n  });\n\n// store\nimport reduxThunk from \"redux-thunk\";\nimport { applyMiddleware, createStore } from \"redux\";\nconst store = createStore(reducer, undefined, applyMiddleware(reduxThunk));\nstore.subscribe((...args) =\u003e {\n  console.log(\"store\", store.getState());\n});\n\n// dispatch\nstore.dispatch(incAsync(1));\n```\n\n### Handle thunk action: createThunkAction\n\n`createThunkAction(...)` returns `{ resolved, rejected, started }` and callable method.\n\n(You need to add `redux-thunk` in store's middlewares)\n\n```js\nimport { createReducer, buildActionCreator } from \"hard-reducer\";\nconst { createThunkAction, createAction } = buildActionCreator();\n\nconst inc = createAction(\"inc\", (val: number) =\u003e val);\n\nconst thunked = createThunkAction(\n  \"thunked\",\n  async (input, dispatch, getState) =\u003e {\n    dispatch(inc(input.value));\n    return { ret: true };\n  }\n);\n\n// Handle\ncreateReducer({ status: \"ready\", payload: null })\n  .case(thunked.started, state =\u003e {\n    return { state: \"started\", payload: null };\n  })\n  .case(thunked.resolved, (state, payload) =\u003e {\n    return { state: \"resolve\", payload };\n  })\n  .case(thunked.rejected, (state, error) =\u003e {\n    return { state: \"ready\", payload: null };\n  });\n\n// dispatch\nstore.dispatch(thunked({ value: 1 }));\n```\n\n## Related projects\n\n- [reduxactions/redux-actions: Flux Standard Action utilities for Redux.](https://github.com/reduxactions/redux-actions)\n- [aikoven/typescript-fsa: Type-safe action creator utilities](https://github.com/aikoven/typescript-fsa)\n- [acdlite/flux-standard-action: A human-friendly standard for Flux action objects.](https://github.com/acdlite/flux-standard-action)\n\n## ChangeLog\n\nSee [ChangeLog.md](ChangeLog.md)\n\n## LICENSE\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmizchi%2Fhard-reducer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmizchi%2Fhard-reducer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmizchi%2Fhard-reducer/lists"}