{"id":13439314,"url":"https://github.com/nefe/iron-redux","last_synced_at":"2025-04-07T13:09:18.899Z","repository":{"id":43162335,"uuid":"140529731","full_name":"nefe/iron-redux","owner":"nefe","description":"Painless typesafe Redux code generator","archived":false,"fork":false,"pushed_at":"2022-08-09T06:36:31.000Z","size":75,"stargazers_count":301,"open_issues_count":1,"forks_count":24,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-31T12:05:24.868Z","etag":null,"topics":["react","redux","redux-state"],"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/nefe.png","metadata":{"files":{"readme":"README-CN.md","changelog":"History.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":"2018-07-11T06:12:09.000Z","updated_at":"2024-07-12T07:29:24.000Z","dependencies_parsed_at":"2022-09-02T20:51:54.852Z","dependency_job_id":null,"html_url":"https://github.com/nefe/iron-redux","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/nefe%2Firon-redux","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nefe%2Firon-redux/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nefe%2Firon-redux/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nefe%2Firon-redux/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nefe","download_url":"https://codeload.github.com/nefe/iron-redux/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247657281,"owners_count":20974345,"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":["react","redux","redux-state"],"created_at":"2024-07-31T03:01:12.878Z","updated_at":"2025-04-07T13:09:18.867Z","avatar_url":"https://github.com/nefe.png","language":"TypeScript","funding_links":[],"categories":["HarmonyOS"],"sub_categories":["Windows Manager"],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://img.alicdn.com/tfs/TB1olCdDAzoK1RjSZFlXXai4VXa-275-183.png\" height=\"64\"\u003e\n  \u003ch2\u003eiron-redux - 一个类型完美的 Redux 去形式化的库。\u003c/h2\u003e\n\u003c/div\u003e\n\n[![npm version](https://badge.fury.io/js/iron-redux.png)](https://badge.fury.io/js/iron-redux)\n[![npm downloads](https://img.shields.io/npm/dt/iron-redux.svg?style=flat-square)](https://www.npmjs.com/package/iron-redux)\n[![Gitter](https://badges.gitter.im/nefe/iron-redux.svg)](https://gitter.im/nefe/iron-redux?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge)\n\n## 特性\n\n- iron-redux 提供了有效的方法来创建类型安全的 redux 类型，巧妙利用 Typescript 的类型推导能力，不需要额外定义任何类型，可以使 redux 整体流程类型完美。\n- 让 Redux 代码极其精简！去除任何冗余的、形式化的代码！参看 [example](https://github.com/nefe/iron-redux/blob/master/examples/redux.tsx)。\n- `ReturnState\u003cyour root reducer map\u003e`自动推导出整个项目的 Redux 全局状态树的类型。\n- 让 reducer 每个 case 都能获取不同的 action 类型，可在 vscode 中参看 [example](https://github.com/nefe/iron-redux/blob/master/examples/redux.tsx)；\n- vscode IDE [插件](https://github.com/nefe/vscode-toolkits)支持。\n- 非常轻量级！源码只有 300 行！零依赖！\n- iron-redux 不仅仅是一个库，同样也是使用 Typescript 编写 Redux 代码的最佳实践，有些规则你必须严格遵守。\n\n## 安装\n\nnpm:\n\n```sh\nnpm i -S iron-redux\n```\n\nyarn:\n\n```sh\nyarn add iron-redux\n```\n\n## 使用方法\n\n## 1. action types\n\n在 iron-redux 中有两种 action 类型：`FetchTypes` 与 `BasicTypes`。\n\n在 enum 中添加一个类型名称就足以定义动作类型:\n\n```js\nenum BasicTypes {\n  changeId\n}\nenum FetchTypes {\n  fetchId,\n  // 这样拿到的 Types.loadData 相当于没有 Error Type。因此 View中可以 .catch\n  loadData = NO_ERROR_TYPES\n}\nconst prefix = 'test/';\nconst Types = composeTypes({\n  prefix,\n  BasicTypes,\n  FetchTypes\n});\n\n// Types.changeId === 'test/changeId';\n// Types.fetchId.success === 'test/changeId_SUCCESS';\n// Types.fetchId.loading === 'test/changeId_LOADING';\n// Types.fetchId.error === 'test/changeId_ERROR';\n```\n\n## 2. action types\n\niron-redux 提供了两种创建 actions 的方法：`createAction`和`createFetchAction`。\n\n### createAction\n\n提供 payload 类型，当它等于 actionCreator 的参数类型时：\n\n```js\nconst actions = {\n  changeId: createAction(Types.changeId)\u003cnumber\u003e()\n};\n\nconsole.log(actions.changeId(3));  // { type: 'test/chagneId', payload: 3 };\n```\n\n当 actionCreator 的参数类型与 payload 类型不相等时，你可以自定义 payload 转换函数。此时参数和返回操作对象都是类型安全的，返回操作对象类型会被自动推断。\n\n```js\nconst actions = {\n  changeId: createAction(Types.changeId)((arg: { id: number, pre: string }) =\u003e {\n    return arg.pre + arg.id;\n  })\n};\n\nconsole.log(actions.changeId({ id: 3, pre: '_' }));\n// { type: 'test/changeId', paylaod: '_3' }\n```\n\n你也可以在 createAction 里指定 state 的属性名，reducer 中提供一个默认的 handleAll 函数，会自动帮你处理 action。\n\n```js\nconst actions = {\n  changeId: createAction(Types.changeId, 'id')\u003cnumber\u003e(),\n};\n\nclass InitialState {\n  id = 3;\n}\n\n// 这样你不需要在reducer中再来处理这个action\n```\n\n当然，如果你不喜欢用 createAction，也可以手写 action。但是记得一定要完善类型不要使用 any\n\n```js\nconst actions = {\n  changeId(id: number) {\n    return { type: Types.changeId, payload: id };\n  }\n};\n```\n\n### createFetchAction\n\n背景：`FetchMiddleware`在 redux 中时非常常见的，如下所示：\n\n```js\n{\n  types: [loadingType, successType, failureType],\n  url: '/api/data',\n  method: 'GET',\n\n}\n```\n\n`FetchMiddleware`同样会根据 API 的返回结果自动处理 loading、success、failure 等 action。\n\n使用如下：\n\n```js\nconst actions = {\n  fetchData: createFetchAction(Types.fetchData, '/api/data', Method.Get)\u003cParams, Response\u003e(),\n};\n```\n\n友情提示: 如果你在使用 [pont](https://github.com/nefe/pont)，那么所有的 fetch action 都将会自动生存。\n\n## 3、initial state\n\n- 1、复杂的属性可以尽量写些注释，方便调用的时候可以辨识\n- 2、使用 `AsyncTuple` 来管理异步获取的数据. InitialState 里不要有各种 loading、error 字段\n- 3、将 initial state 命名为 `State`，这样可以同时产生 state 的初始值以及 state 的类型定义。\n\n```typescript\nclass State {\n  /** comment the property here */\n  isDialogVisible = false;\n  detailFuncInfo = new AsyncTuple(API.ideFunction.getDetailById.init);\n  id = 0;\n}\n```\n\n## 4、reducer\n\n[toolkits](https://github.com/nefe/vscode-toolkits) VSCode 插件会帮助你生成以上所有的 snippets 代码\n\n```js\nfunction reducer(state = new InitialState(), action: ActionType\u003ctypeof actions\u003e): InitialState {\n  switch (action.type) {\n    case Types.addNum: {\n      const num = action.payload;\n\n      return {\n        ...state,\n        num\n      };\n    }\n    default: {\n      return AsyncTuple.handleAll(prefix, state, action);\n    }\n  }\n}\n```\n\n友情提示：在每一种 case 中，你都可以针对不同的常见尝试不同的 payload 类型。\n\n## 5、AsyncTuple\n\n`AsyncTuple` 会帮助你管理所有的 loading，error，message，data 等类型数据。\n\n```js\nclass InitialState {\n  data = new AsyncTuple(someResponse);\n}\n```\n\n同时`AsyncTuple`提供了 `AsyncTuple.handleLoading`, `AsyncTuple.handleError`, `AsyncTuple.handleSuccess`等静态方法，帮助你处理 API 请求过程中的不同逻辑。\n\n```js\ncase Types.loadData.loading: {\n  return AsyncTuple.handleLoading(\"data\", state);\n}\ncase Types.loadData.success: {\n  return AsyncTuple.handleSuccess(\"data\", state, action);\n}\ncase Types.loadData.error: {\n  return AsyncTuple.handleError(\"data\", state, action);\n}\n```\n\n`AsyncTuple` 提供了一个强大的方法 `handleAll`来帮助你处理所有的 API 请求逻辑。但前提是你必须使用`AsyncTuple` 初始化你的 state。\n\n```js\nconst actions = {\n  // define state field\n  fetchData: createFetchAction(Types.fetchData, '/api/data', Method.Get)\u003cParams, Response\u003e('listData'),\n};\n\nclass InitialState {\n  // using AsyncTuple\n  listData = new AsyncTuple();\n}\n\n/**\n * reducer\n */\nfunction reducer(\n  state = new InitialState(),\n  action: ActionType\u003ctypeof actions\u003e\n): InitialState {\n  switch (action.type) {\n    // you don't need write any API fetch logic here!\n    default: {\n      return AsyncTuple.handleAll(prefix, state, action);\n    }\n  }\n}\n```\n\n## 获取 redux 全局状态类型\n\n项目中所有的 reducers 文件都会有一个根 reducer 文件。iron-redux 提供了一个自动推断类型的接口`ReturnState`，它会自动推导出整个项目的 Redux 全局状态树的类型。\n\n```js\nconst rootReducers = {\n  a: AReducer,\n  b: BReducer\n};\nconst rootReducer = combineReducer(rootReducers);\n\nexport type RootState = ReturnState\u003ctypeof rootReducers\u003e;\n```\n\n友情提示：如果该方法不生效，请检查你的 redux 版本。老的 redux 版本会出现一个`combineReducer`类型错误。\n\n## safeGet\n\n与 lodash.get 方法一样，但类型完美。\n\n```js\nconst deepObj = {\n  obj: {\n    arr: [\n      {\n        num: 3\n      }\n    ]\n  },\n  obj2: {\n    str: ''\n  }\n};\n\n// get data path is type safe\nconst num = safeGet(deepObj, ['obj', 'arr', 0, 'num'], defaultValueHere);\nconst str = safeGet(deepObj, ['obj2', 'str']);\n// return type is type safe\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnefe%2Firon-redux","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnefe%2Firon-redux","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnefe%2Firon-redux/lists"}