{"id":19120083,"url":"https://github.com/hrsh7th/srimmer","last_synced_at":"2025-10-24T08:32:35.912Z","repository":{"id":57368628,"uuid":"151850721","full_name":"hrsh7th/srimmer","owner":"hrsh7th","description":"Srimmer provides simple api to use react and immer.","archived":false,"fork":false,"pushed_at":"2018-10-25T18:07:49.000Z","size":36,"stargazers_count":6,"open_issues_count":5,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-05-01T20:20:50.875Z","etag":null,"topics":["context-api","immer","react","state-management"],"latest_commit_sha":null,"homepage":null,"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/hrsh7th.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":"2018-10-06T14:58:57.000Z","updated_at":"2022-12-26T14:42:35.000Z","dependencies_parsed_at":"2022-09-05T20:41:14.759Z","dependency_job_id":null,"html_url":"https://github.com/hrsh7th/srimmer","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hrsh7th%2Fsrimmer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hrsh7th%2Fsrimmer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hrsh7th%2Fsrimmer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hrsh7th%2Fsrimmer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hrsh7th","download_url":"https://codeload.github.com/hrsh7th/srimmer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223786711,"owners_count":17202603,"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":["context-api","immer","react","state-management"],"created_at":"2024-11-09T05:12:37.548Z","updated_at":"2025-10-24T08:32:35.840Z","avatar_url":"https://github.com/hrsh7th.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Srimmer\n\nSrimmer provides simple api to use react, immer and TypeScript.\n\ninspired `react-copy-write`.\n\n# API\n\n```typescript\nimport { define, Select } from \"srimmer\";\n\n/**\n * Extract consumer's selected state. `Select\u003ctypeof Consumer\u003e`\n */\nexport { Select };\n\n/**\n * Your state.\n */\ntype State = {\n  todos: {\n    name: string;\n    status: string;\n  }[];\n};\n\n/**\n * Define some state utilities from your state type.\n */\nconst defined = define\u003cState\u003e();\n\nexport const {\n  /**\n   * State provider.\n   * @type {React.ComponentType\u003c{ state: State; }\u003e}\n   */\n  Provider,\n\n  /**\n   * State updater.\n   * @type {(updater: (state: State) =\u003e void) =\u003e void}\n   */\n  update,\n\n  /**\n   * immer's patchListener.\n   * @type {(patchListener: PatchListener) =\u003e void}\n   */\n  patches,\n\n  /**\n   * State selector.\n   * @type {\u003cT\u003e(select: (state: State) =\u003e T) =\u003e Consumer\u003cT\u003e}\n   */\n  select,\n\n  /**\n   * State getter.\n   * Note: Carefully. It makes implicit depends to state values.\n   * @type {() =\u003e State}\n   */\n  get,\n\n  /**\n   * State setter.\n   * Note: For testing.\n   * @type {(state: State) =\u003e void}\n   */\n  set\n} = defined;\n```\n\n# Real World Usage\n\n## define your state (src/state/index.ts)\n\n```typescript\nimport { define, Select } from 'srimmer';\n\nexport type State = { ... };\n\nexport const {\n  Provider,\n  select,\n  update,\n  patches,\n  get,\n  set\n} = define\u003cState\u003e();\n\nexport { Select };\n```\n\n## define your action (src/action/index.ts)\n\n```typescript\nimport { update } from \"../../state\";\n\nexport const addNewTask = () =\u003e {\n  update(state =\u003e {\n    state.todos.push({\n      name: `new todo ${state.todos.length}`,\n      status: \"todo\"\n    });\n  });\n};\n```\n\n## bootstrap (src/index.tsx)\n\n```typescript\nimport React from \"react\";\nimport ReactDOM from \"react-dom\";\nimport { Provider } from \"./state\";\n\nReactDOM.render(\n  \u003cProvider state={createInitialState()}\u003e\n    \u003cApp /\u003e\n  \u003c/Provider\u003e,\n  document.getElementById(\"app\")!\n);\n\nfunction createInitialState() {\n  return JSON.stringify(document.getElementById(\"app\")!.getAttribute(\"data\"));\n}\n```\n\n## consume state (src/component/\\*_/_.tsx)\n\n```typescript\nimport { select, Select } from '../../state';\nimport { addNewTask } from '../action';\n\nconst Consumer = select(state =\u003e ({\n  todos: state.todos\n}));\n\nexport default () =\u003e (\n  \u003cConsumer\u003e\n    {state =\u003e (\n      \u003cbutton onClick={() =\u003e onAddButtonClick(state)}\u003eadd\u003c/button\u003e\n      \u003cdiv\u003e{todos(state)}\u003c/div\u003e\n    )}\n  \u003c/Consumer\u003e\n);\n\nconst todos = (state: Select\u003ctypeof Consumer\u003e) =\u003e {\n  return state.todos.map(todo =\u003e (\n    \u003cdiv key={todo.id}\u003e{todo.name} - {todo.status}\u003c/div\u003e\n  ));\n}\n\nconst onAddButtonClick = () =\u003e {\n  addNewTask();\n};\n```\n\n## testing your updater (test/action/index.test.ts)\n\n```typescript\nimport diff from 'snapshot-diff'; # https://github.com/jest-community/snapshot-diff\nimport { set, get } from '../../../src/state';\nimport { addNewTask } from '../../../src/action';\n\nbeforeEach(() =\u003e {\n  set({ ...fixture });\n});\n\ntest('addNewTask', () =\u003e {\n  const state = get()!;\n  addNewTask();\n  expect(diff(state, get()!)).toMatchSnapshot();\n});\n```\n\n# Recommended Structure\n\n```bash\n/src\n  /state      # State schemas and querying utility functions.\n    index.ts\n  /action     # State updators.\n    index.ts\n  /component  # State selectors.\n    index.tsx\n  index.tsx   # Bootstrap.\n```\n\nSee `hrsh7th/ganttcharty`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhrsh7th%2Fsrimmer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhrsh7th%2Fsrimmer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhrsh7th%2Fsrimmer/lists"}