{"id":14483725,"url":"https://github.com/jaystack/repatch","last_synced_at":"2025-10-26T00:35:10.731Z","repository":{"id":57353617,"uuid":"95385096","full_name":"jaystack/repatch","owner":"jaystack","description":"Dispatch reducers","archived":false,"fork":false,"pushed_at":"2017-11-08T15:33:23.000Z","size":136,"stargazers_count":507,"open_issues_count":0,"forks_count":12,"subscribers_count":25,"default_branch":"master","last_synced_at":"2024-04-26T02:40:52.031Z","etag":null,"topics":["async-actions","data-management","dataflow","dispatch","middleware","react","reducer","redux","redux-thunk","thunk"],"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/jaystack.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-06-25T20:50:11.000Z","updated_at":"2024-02-04T18:15:06.000Z","dependencies_parsed_at":"2022-09-05T13:11:18.796Z","dependency_job_id":null,"html_url":"https://github.com/jaystack/repatch","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaystack%2Frepatch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaystack%2Frepatch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaystack%2Frepatch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaystack%2Frepatch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jaystack","download_url":"https://codeload.github.com/jaystack/repatch/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247166168,"owners_count":20894654,"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":["async-actions","data-management","dataflow","dispatch","middleware","react","reducer","redux","redux-thunk","thunk"],"created_at":"2024-09-03T00:02:02.946Z","updated_at":"2025-10-04T19:35:30.853Z","avatar_url":"https://github.com/jaystack.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# \u003ca href=\"https://www.npmjs.com/package/repatch\"\u003e\u003cimg alt=\"Repatch\" src=\"http://jaystack.com/wp-content/uploads/2017/08/repatch-logo.png\" height=\"50px\"\u003e\u003c/a\u003e\n\n[![npm version](https://img.shields.io/npm/v/repatch.svg?style=flat-square)](https://www.npmjs.com/package/repatch)\n[![npm downloads](https://img.shields.io/npm/dm/repatch.svg?style=flat-square)](https://www.npmjs.com/package/repatch)\n[![corp-check status](https://api.corp-check.corpjs.com/badge?name=repatch)](https://corp-check.corpjs.com/result?name=repatch)\n\n## Dispatch reducers\n\n[Repatch](https://www.npmjs.com/package/repatch) is just a simplified [Redux](https://www.npmjs.com/package/redux), that let you create actions more briefly by dispatching reducers directly.\n\n\u003cimg alt=\"draft\" src=\"http://jaystack.com/wp-content/uploads/2017/08/repatch_pl-hand-d2-e1503663114155.png\" width=\"40%\"\u003e\n\n```javascript\nstore.dispatch(state =\u003e ({ ...state, counter: state.counter + 1 }));\n```\n\n**In this terminology, an action is a function that returns a reducer:**\n\n```javascript\nconst increment = amount =\u003e state =\u003e ({\n  ...state,\n  counter: state.counter + amount\n});\n\nstore.dispatch(increment(42));\n```\n\n## Motivation\n\n[Redux](https://www.npmjs.com/package/redux) has verbose action management. The most of redux projects do not need sctrict action administration. Action types, action creators and the reducer's action handlers are mutually assigned to each other. Repatch's purpose is creating actions briefly.\n\nThe simplest way to keep the immutable action controlled dataflow and define actions briefly is dispatching pure functions (as reducers) to the store.\n\n## Comparison with [Redux](https://www.npmjs.com/package/redux)\n\nRepatch is\n- **less verbose**\n- **smaller** (the minified version is __less than 1 KB__)\n- [**faster**](https://github.com/jaystack/redux-repatch-performance-comparison)\n\nthan Redux.\n\n## Working with [Redux](https://www.npmjs.com/package/redux)\n\nIf you have to keep the official Redux in your project, then you can use the [redux-repatch](https://www.npmjs.com/package/redux-repatch) or [redux-repatch-creator](https://www.npmjs.com/package/redux-repatch-creator) enhancers.\n\n## [API Reference](https://github.com/jaystack/repatch/wiki)\n\n## Examples\n\n- [JavaScript example](https://github.com/jaystack/repatch-example-electron-app)\n- [TypeScript example](https://github.com/jaystack/repatch-example-electron-app-ts)\n\n## Articles\n\n[Repatch - the simplified Redux](https://community.risingstack.com/repatch-the-simplified-redux/)\n\n## Installation\n\n```\nnpm install --save repatch\n```\n\n## How to use\n\n### ES6\n\n```javascript\nimport Store from 'repatch';\n\nconst store = new Store(initialState);\n```\n\n### CommonJS\n\n```javascript\nconst Store = require('repatch').Store;\n```\n\n### UMD\n\n```html\n\u003cscript src=\"https://unpkg.com/repatch/dist/repatch.js\"\u003e\u003c/script\u003e\n```\n\nor the minified bundle:\n\n```html\n\u003cscript src=\"https://unpkg.com/repatch/dist/repatch.min.js\"\u003e\u003c/script\u003e\n```\n\nand\n\n```javascript\nconst Store = Repatch.Store;\nconst thunk = Repatch.thunk;\n```\n\n## Compatibility with [react-redux](https://www.npmjs.com/package/react-redux)\n\nRepatch's interface is very similar to Redux, therefore you can use with [react-redux](https://www.npmjs.com/package/react-redux).\n\n```javascript\nconst unsubscribe = store.subscribe(() =\u003e console.log(store.getState()));\n\nstore.dispatch(resolveFetchingUsers(users));\n\nunsubscribe();\n```\n\n## TODO app in brief\n\n```javascript\nconst store = new Store([]);\n\nconst addTodo = text =\u003e todos =\u003e [...todos, { text, checked: false }];\n\nconst checkTodo = index =\u003e todos =\u003e todos.map(\n  (todo, i) =\u003e (i === index ? { ...todo, checked: !todo.checked } : todo)\n);\n\nconst editTodo = (index, text) =\u003e todos =\u003e todos.map(\n  (todo, i) =\u003e (i === index ? { ...todo, text } : todo)\n);\n\nconst removeTodo = index =\u003e todos =\u003e todos.filter((_, i) =\u003e i !== index);\n```\n\n## Sub-reducers\n\nWe do not need to reduce always the whole state of the store. Repatch also offers a way to combine sub-reducers, those describe a deeply nested property in the state. We just define a helper function that takes a nested reducer as argument, and returns a reducer that reduces the whole state:\n\n```javascript\nconst reduceFoo = fooReducer =\u003e state =\u003e ({\n  ...state,\n  bar: {\n    ...state.bar,\n    foo: fooReducer(state.bar.foo)\n  }\n});\n```\n\nUsing that we can define easily an action, that sets an `x` property in the `foo` object:\n\n```javascript\nconst setX = x =\u003e reduceFoo(state =\u003e ({ ...state, x }));\n```\n\n## Middlewares\n\nA repatch middleware takes the `store` instance, a `next` function and the previous `reducer`. The middleware can provide a new reducer via the `next` function.\n\n```javascript\nMiddleware: Store -\u003e Next -\u003e Reducer -\u003e any\n```\n\nUse the `addMiddleware` method to chaining middlewares:\n\n```javascript\nconst store = new Store(initialState)\n  .addMiddleware(mw1)\n  .addMiddleware(mw2, mw3);\n```\n\n## Middleware example\n\nThis simple logger middleware logs the current- and the next state:\n\n```javascript\nconst logger = store =\u003e next =\u003e reducer =\u003e {\n  const state = store.getState()\n  const nextState = reducer(state)\n  console.log(state, nextState)\n  return next(_ =\u003e nextState)\n}\n\nconst store = new Store(initialState).addMiddleware(logger)\n```\n\n## Async actions\n\nThe `thunk` middleware is useful for handling async actions similar to [redux-thunk](https://www.npmjs.com/package/redux-thunk).\n\n```javascript\nimport Store, { thunk } from 'repatch';\n\nconst store = new Store(initialState).addMiddleware(thunk);\n```\n\nIn thunk async actions reducer returns a function (*delegate*):\n\n```javascript\nconst updateUser = delta =\u003e state =\u003e async (dispatch, getState) =\u003e {\n  try {\n    const editedUserId = getState().editedUser;\n    dispatch(toggleSpinner(true));\n    await api.updateUser(editedUserId, delta);\n    await dispatch(fetchUsers());\n  } catch (error) {\n    dispatch(state =\u003e ({ ...state, error: error.message }))\n  } finally {\n    dispatch(toggleSpinner(false));\n  }\n};\n```\n\nIt is possible to embed async actions within each other too and awaiting their resolving:\n\n```javascript\nawait dispatch(fetchUsers());\n```\n\n### Injecting extra argument\n\nIt is possible to inject extra arguments into async actions:\n\n```javascript\nimport Store, { thunk } from 'repatch';\nimport api from './api';\nimport { hashHistory } from 'react-router';\n\nconst store = new Store(initialState)\n  .addMiddleware(thunk.withExtraArgument({ api, hashHistory }));\n```\n\nThen you can access these arguments in your delegates:\n\n```javascript\nconst updateUser = delta =\u003e state =\u003e\n  async (dispatch, getState, { api, hashHistory }) =\u003e {\n    // ...\n  }\n```\n\nThis way you can keep your async actions independently from outer instances or side-effects. This practice is useful for testing.\n\n## Testing\n\n### Sync actions\n\nTesting a reducer is easy:\n\n```javascript\nimport * as assert from 'assert';\nimport { changeName } from './actions';\n\n// ...\n\nit('changeName', () =\u003e {\n  const state = { name: 'john' };\n  const nextState = changeName('jack')(state);\n  assert.strictEqual(nextState.name, 'jack');\n});\n```\n\n### Async actions\n\nFor async action tests you need to instantiate the `Store` and provide mocked extra arguments.\n\n```javascript\nimport Store, { thunk } from 'repatch';\nimport * as assert from 'assert';\nimport { fetchUsers } from './actions';\n\nconst mockUsers = [{ username: 'john' }];\nconst mockApi = {\n  getUsers: () =\u003e Promise.resolve(mockUsers)\n}\n\n// ...\n\nit('fetchUsers', async () =\u003e {\n  const state = { users: [] };\n  const store = new Store(state)\n    .addMiddleware(thunk.withExtraArgument({ api: mockApi }));\n  await store.dispatch(fetchUsers());\n  const nextState = store.getState();\n  assert.deepEqual(nextState.users, mockUsers);\n});\n```\n\n---\n\n## License\n\n[MIT](https://spdx.org/licenses/MIT)\n\n## Community\n\n[https://twitter.com/repatchjs](https://twitter.com/repatchjs)\n\n## Developed by\n\n[![JayStack](http://jaystack.com/wp-content/uploads/2017/08/jaystack_logo_transparent_50.png)](http://jaystack.com/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaystack%2Frepatch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjaystack%2Frepatch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaystack%2Frepatch/lists"}