{"id":21004811,"url":"https://github.com/halfzebra/effekt","last_synced_at":"2025-12-29T23:29:29.915Z","repository":{"id":73687828,"uuid":"143883638","full_name":"halfzebra/effekt","owner":"halfzebra","description":" ➰ Redux enhancer for state-driven I/O","archived":false,"fork":false,"pushed_at":"2018-08-12T20:47:22.000Z","size":29,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-20T10:21:03.971Z","etag":null,"topics":["redux","side-effects","typescript"],"latest_commit_sha":null,"homepage":"","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/halfzebra.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":"2018-08-07T14:17:53.000Z","updated_at":"2018-08-12T20:47:23.000Z","dependencies_parsed_at":"2023-02-24T01:15:49.600Z","dependency_job_id":null,"html_url":"https://github.com/halfzebra/effekt","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/halfzebra%2Feffekt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/halfzebra%2Feffekt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/halfzebra%2Feffekt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/halfzebra%2Feffekt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/halfzebra","download_url":"https://codeload.github.com/halfzebra/effekt/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243420924,"owners_count":20288173,"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":["redux","side-effects","typescript"],"created_at":"2024-11-19T08:38:05.081Z","updated_at":"2025-12-29T23:29:29.887Z","avatar_url":"https://github.com/halfzebra.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ➰ effekt [![Build Status](https://travis-ci.org/halfzebra/effekt.svg?branch=master)](https://travis-ci.org/halfzebra/effekt)\n\n[Redux](https://redux.js.org/) enhancer for state-driven I/O.\n\n## Motivation\n\n_effect_ is an attempt to combine the simplicity of [redux-thunk](https://github.com/reduxjs/redux-thunk) with the potential of [redux-saga](https://github.com/redux-saga/redux-saga).\n\nCreated for those, who find the existing solutions for I/O in Redux ecosystem are too dense or too cluttered with unnecessary abstractions.\n\nIt leverages the core JavaScript Data Structures and standard language features while tailored to be used with [TypeScript](#typescript)(but it's not a hard requirement).\n\n## Glossary\n\nCommand is a function that accepts state and returns a Promise of some asynchronous computation that resolves into an Action.\n\nUpdate is a reducer that returns a JavaScript array with two items, first representing the next State and the second being the Command.\n\n```typescript\nfunction update\u003cS, A\u003e(state: S, action: A): [ S, S =\u003e Promise\u003cA\u003e ] | [  S ]\n```\n\n## Usage\n\n### JavaScript\n\n`toDoViewerUpdate.js`\n```js\n\n// Actions.\nconst Fetch = payload =\u003e ({ type: 'Fetch', payload })\nconst Success = payload =\u003e ({ type: 'Success', payload })\nconst Fail = (payload: string): Success =\u003e ({ type: 'Fail', payload })\n\nfunction update(state, action) {\n  const { type, payload } = action;\n  switch (type) {\n    case 'Fetch':\n      return [\n        state,\n        () =\u003e\n          fetch(`https://jsonplaceholder.typicode.com/todos/${payload}`)\n            .then(response =\u003e response.json())\n            .then(({ title }) =\u003e title)\n            .then(Success)\n            .catch(({ message}) =\u003e Fail(message))\n      ];\n      \n    case 'Success':\n       return [ { title: payload } ]\n      \n    case 'Fail':\n       return [ { error: payload } ]\n      \n    default:\n      return [ state ]\n  }\n}\n```\n\n[eslint-plugin-promise](https://github.com/xjamundx/eslint-plugin-promise) with [`\"catch-or-return\"`](https://github.com/xjamundx/eslint-plugin-promise/blob/master/docs/rules/catch-or-return.md) and [`\"always-return\"`](https://github.com/xjamundx/eslint-plugin-promise/blob/master/docs/rules/always-return.md) rules are very helpful.\n\n### TypeScript\n\n`toDoViewerUpdate.ts`\n```typescript\n\n// Actions.\ntype Fetch = { type: 'Fetch', payload: number }\ntype Fail = { type: 'Fail', payload: string }\ntype Success = { type: 'Success', payload: string }\n\ntype Action = Fetch | Fail | Success\n\nconst Fetch = (payload: number): Fetch =\u003e ({ type: 'Fetch', payload })\nconst Success = (payload: string): Success =\u003e ({ type: 'Success', payload })\nconst Fail = (payload: string): Fail =\u003e ({ type: 'Fail', payload })\n\n// State machine definition.\ntype State = { error: string } | { title: string } | null;\n\ntype Command = () =\u003e Promise\u003cSuccess | Fail\u003e\n\n// Update function that combines the traditional reducer and asynchronous command.\nfunction update(state: State = null, action: Action) : [ State, Command ] | [ State ] {\n  const { type, payload } = action;\n  switch (type) {\n    case 'Fetch':\n      return [\n        state,\n        () =\u003e\n          fetch(`https://jsonplaceholder.typicode.com/todos/${payload}`)\n            .then(response =\u003e response.json())\n            .then(({ title }) =\u003e title)\n            .then(Success)\n            .catch(({ message }) =\u003e Fail(message))\n      ];\n      \n    case 'Success':\n       return [ { title: payload } ]\n      \n    case 'Fail':\n       return [ { error: payload } ]\n      \n    default:\n      return [ state ]\n  }\n}\n```\n\n`ToDoViewer.ts`\n```typescript\nconst Viewer = ({ state, Fetch }) =\u003e (\n  \u003cReact.Fragment\u003e\n    { state.error \u0026\u0026 \u003cp\u003eWe have an error!\u003c/p\u003e }\n    { state.title \u0026\u0026 \u003cp\u003e{ state.title }\u003c/p\u003e }\n    \u003cbutton onClick={() =\u003e Fetch(1)}\u003eFirst\u003c/button\u003e\n    \u003cbutton onClick={() =\u003e Fetch(2)}\u003eSecond\u003c/button\u003e\n    \u003cbutton onClick={() =\u003e Fetch(3)}\u003eThird\u003c/button\u003e\n  \u003c/React.Fragment\u003e\n)\n```\n\n## Acknowledgements\n\n- [The Elm Architecture](https://guide.elm-lang.org/architecture/)\n- [redux-loop](https://github.com/redux-loop/redux-loop)\n- [redux-observable](https://redux-observable.js.org/)\n- [redux-saga](https://github.com/redux-saga/redux-saga)\n- [redux-promise-middleware](https://github.com/pburtchaell/redux-promise-middleware)\n- [redux-thunk](https://github.com/reduxjs/redux-thunk)\n- [react-redux-typescript-guide](https://github.com/piotrwitek/react-redux-typescript-guide)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhalfzebra%2Feffekt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhalfzebra%2Feffekt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhalfzebra%2Feffekt/lists"}