{"id":15391813,"url":"https://github.com/antonk52/rainbow-actions","last_synced_at":"2025-04-15T23:24:32.413Z","repository":{"id":51265532,"uuid":"339813353","full_name":"antonk52/rainbow-actions","owner":"antonk52","description":"Redux actions + TypeScript = \u003c3","archived":false,"fork":false,"pushed_at":"2022-03-25T14:45:42.000Z","size":410,"stargazers_count":17,"open_issues_count":1,"forks_count":4,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-02-07T13:36:00.123Z","etag":null,"topics":["immer","redux","redux-actions","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/antonk52.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2021-02-17T18:09:09.000Z","updated_at":"2024-10-23T10:13:06.000Z","dependencies_parsed_at":"2022-09-07T17:35:23.855Z","dependency_job_id":null,"html_url":"https://github.com/antonk52/rainbow-actions","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/antonk52%2Frainbow-actions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonk52%2Frainbow-actions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonk52%2Frainbow-actions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonk52%2Frainbow-actions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/antonk52","download_url":"https://codeload.github.com/antonk52/rainbow-actions/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240351217,"owners_count":19787794,"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":["immer","redux","redux-actions","typescript"],"created_at":"2024-10-01T15:13:08.060Z","updated_at":"2025-02-28T18:31:25.424Z","avatar_url":"https://github.com/antonk52.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Rainbow actions 🌈\n\n[![npm version](https://img.shields.io/npm/v/rainbow-actions.svg?style=flat)](https://www.npmjs.com/package/rainbow-actions)\n[![npm license](https://img.shields.io/npm/l/rainbow-actions.svg?style=flat)](https://www.npmjs.com/package/rainbow-actions)\n\nThis is **not** a project to reduce redux boilerplate. The project goal is to gather the same domain actions and action creators in namespaces by their domain purpose.\n\n## Install\n\n```shell\nnpm install rainbow-actions\n```\n\n## Basic usage\n\n```typescript\nimport {createActions} from 'rainbow-actions'\n\ntype PostId = string\n\ntype PayloadDictionary = {\n    init: PostId;\n    //    ^^^^^^ action creator payload\n    fulfill: {id: PostId; title: string};\n    error: {code: number};\n    fail: never;\n    //    ^^^^^ action creator has no payload\n}\n\nconst requestPost = createActions\u003cPayloadDictionary\u003e()('get_post')\n\n/**\n * runtime value   `'get_post_init'`\n * typescript type `'get_post_init'`\n */\nconst initType = requestPost.init.type\n\n/**\n * runtime value   `{type: 'get_post_init', payload: '42'}`\n * typescript type `{type: 'get_post_init', payload: PostId}`\n */\nconst initAction = requestPost.init('42')\n```\n\nTypescript raises an error when one attempts to access a property of `requestPost` any other than the keys of `PayloadDictionary`.\n\n## Extract action types\n\n```typescript\nimport {createActions, ExtractNamespaceActions, ExtractManyNamespaceActions} from 'rainbow-actions'\n\ntype RequestPayloads = {\n    init: number;\n    get: string;\n    end: never;\n}\n\nconst request = createActions\u003cRequestPayloads\u003e()('request')\n\n/**\n * To extract action types from a single namespace you can use `ExtractNamespaceActions`\n */\ntype Actions = ExtractNamespaceActions\u003ctypeof request\u003e\n\nActions // {type: 'request_init'; payload: number} | {type: 'request_get'; payload: string} | {type: 'request_end'}\n\n/**\n * To extract action types from multiple namespaces you can use `ExtractManyNamespaceActions`\n */\ntype AllActions = ExtractManyNamespaceActions\u003c[typeof request, typeof anotherNamespace /*...etc*/]\u003e\n```\n\n## Advanced usage\n\nNote the object passed to the function after the action type base:\n\n```typescript\nimport {createActions} from 'rainbow-actions'\n\ntype PayloadDictionary = {\n    one: number;\n    two: boolean;\n    three: string;\n}\n\n/**\n * You can see that using the optional action dictionary argument you can define:\n * * payload - a function to generate/modify payload\n * * meta - a function to generate/modify meta information\n * * error - a flag for error actions\n *\n * You can define the ones you need or omit them alltogether\n */\nconst base = createActions\u003cPayloadDictionary\u003e()('base', {\n    one: {payload: (id) =\u003e id * 2},\n    //              ^^ the type inferred as number since we defined it in PayloadDictionary\n    two: {meta: (flag) =\u003e `flag is ${flag}`},\n    three: {error: true},\n})\n\n/**\n * runtime value   `{type: 'base_one', payload: 10}`\n * typescript type `{type: 'base_one', payload: number}`\n */\nconst one = base.one(5)\n\n/**\n * runtime value   `{type: 'base_two', payload: true, meta: 'flag is true'}`\n * typescript type `{type: 'base_two', payload: boolean, meta: string}`\n */\nconst two = base.two(true)\n\n/**\n * runtime value   `{type: 'base_three', payload: 'hey', error: true}`\n * typescript type `{type: 'base_three', payload: string, error: true}`\n */\nconst three = base.three('hey')\n```\n\n## Caveat\n\nBoth basic and advanced usage work using the Proxy object. This means if you are not 100% confident about your types, then developers may accidentally dispatch incorrect actions since any method on the created action namespace will be a valid action creator. If this sounds scary, worry no more, this package also provides a safe version.\n\n## Safe usage\n\nIf you prefer to avoid the Proxy usage or are not 100% confident in the typescript types in your project, you can use the safe version. It requires to pass all of the actions for the namespace to be accessible.\n\n```typescript\nimport {createActions} from 'rainbow-actions/safe'\n//                     note the import path  ^^^^\n\ntype PayloadDictionary = {\n    none: never;\n    one: number;\n    two: boolean;\n    three: string;\n}\n\nconst base = createActions\u003cPayloadDictionary\u003e()('base', {\n    none: 0,\n    //    ^ if no creators are necessary, 0 can be a placeholer\n    one: {payload: (id) =\u003e id * 2},\n    two: {meta: (flag) =\u003e `flag is ${flag}`},\n    three: {error: true},\n})\n\n/**\n * runtime value   `{type: 'base_none'}`\n * typescript type `{type: 'base_none'}`\n */\nconst none = base.none()\n\n/**\n * runtime value   `{type: 'base_one', payload: 10}`\n * typescript type `{type: 'base_one', payload: number}`\n */\nconst one = base.one(5)\n\n/**\n * runtime value   `{type: 'base_two', payload: true, meta: 'flag is true'}`\n * typescript type `{type: 'base_two', payload: boolean, meta: string}`\n */\nconst two = base.two(true)\n\n/**\n * runtime value   `{type: 'base_three', payload: 'hey', error: true}`\n * typescript type `{type: 'base_three', payload: string, error: true}`\n */\nconst three = base.three('hey')\n```\n\n## Reducers\n\nThere are two ways one may want to write reducers [redux-actions like](#redux-actions-like-reducers) or [immer way](#immer-reducers)\n\n## Redux actions like reducers\n\n```typescript\nimport {handleActions} from 'rainbow-actions/reducer'\nimport {request} from './actions'\nimport type {Actions} from './actions'\nimport type {State} from './state'\n\nconst defaultState = {}\n\nexport const reducer = handleActions\u003cState, Actions\u003e(\n    {\n        [request.init.type]: (state, action) =\u003e ({\n            ...state,\n            count: action.payload,\n            isLoading: true,\n        })\n        // other handlers\n    },\n    defaultState,\n)\n```\n\n## Immer reducers\n\nOn the other hand you can use [`immer`](https://github.com/immerjs/immer). Immer is a peer dependency and you have to have it installed in your project.\n\n```typescript\nimport {handleActions} from 'rainbow-actions/immer'\nimport {request} from './actions'\nimport type {Actions} from './actions'\nimport type {State} from './state'\n\nconst defaultState = {}\n\nexport const reducer = handleActions\u003cState, Actions\u003e(\n    {\n        [request.init.type]: (state, action) =\u003e {\n            state.count = action.payload\n            state.isLoading = true\n        }\n        // other handlers\n    },\n    defaultState,\n)\n```\n\n## Acknowledgments\n\nThis package is highly inspired by [typed-actions](https://github.com/lttb/typed-actions), [piler](https://github.com/lttb/piler) by [@lttb](https://github.com/lttb), and [immer](https://github.com/immerjs/immer) by [@mweststrate](https://github.com/mweststrate).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantonk52%2Frainbow-actions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fantonk52%2Frainbow-actions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantonk52%2Frainbow-actions/lists"}