{"id":13828935,"url":"https://github.com/michael-klein/forimmer","last_synced_at":"2025-07-04T21:39:46.057Z","repository":{"id":36316866,"uuid":"222081094","full_name":"michael-klein/forimmer","owner":"michael-klein","description":"Immutable state store for react apps with suspense integration.","archived":false,"fork":false,"pushed_at":"2023-01-05T00:59:07.000Z","size":1629,"stargazers_count":14,"open_issues_count":15,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-03T19:40:20.279Z","etag":null,"topics":["immer","javascript","state-management","store","suspense","typescript"],"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/michael-klein.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}},"created_at":"2019-11-16T10:13:59.000Z","updated_at":"2022-03-31T19:49:35.000Z","dependencies_parsed_at":"2023-01-17T01:16:52.176Z","dependency_job_id":null,"html_url":"https://github.com/michael-klein/forimmer","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/michael-klein%2Fforimmer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michael-klein%2Fforimmer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michael-klein%2Fforimmer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michael-klein%2Fforimmer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/michael-klein","download_url":"https://codeload.github.com/michael-klein/forimmer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250790174,"owners_count":21487762,"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","javascript","state-management","store","suspense","typescript"],"created_at":"2024-08-04T09:03:21.300Z","updated_at":"2025-04-25T09:31:59.577Z","avatar_url":"https://github.com/michael-klein.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eforimmer \u003cimg alt=\"build status\" src=\"https://travis-ci.org/michael-klein/forimmer.svg?branch=master\" /\u003e \u003ca href=\"https://www.npmjs.com/package/forimmer\" rel=\"nofollow\"\u003e \u003cimg alt=\"npm\" src=\"https://camo.githubusercontent.com/c374bcf36b575ebdbcf7115f0127333f32e99cdb/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f666f72696d6d6572\" data-canonical-src=\"https://img.shields.io/npm/v/forimmer\" style=\"max-width:100%;\"\u003e\u003c/a\u003e \u003cimg alt=\"types\" src=\"https://badgen.net/npm/types/tslib\"\u003e\u003c/h1\u003e\n\u003cdiv align=\"center\"\u003e\u003cb\u003eImmutable state store for react apps with suspense integration.\u003c/b\u003e\u003c/div\u003e\n\n## What is this?\n\n**forimmer** is a experimental functional state store for react apps, which uses [immer](https://github.com/immerjs/immer) for immutability and integrates with the new react [suspense](https://reactjs.org/docs/concurrent-mode-suspense.html) API. It was in part inspired by the excellent [pullstate](https://github.com/lostpebble/pullstate).\n\nThe name is just a silly play with words. \"immer\" means \"always\" or \"ever\" in German and \"für immer\" means \"forever\". So, forimmer is basically just a fun, bad translation (that you might encounter from actual Germans trying to speak English). I'm not good at naming things.\n\n## How does it work?\n\nFirst, here is a codesandbox with a very contrived example:\n[link](https://codesandbox.io/s/mystifying-leaf-bec9j?fontsize=14\u0026hidenavigation=1\u0026theme=dark)\n\nYou can install **forimmer** from npm:\n```bash\nnpm install forimmer -s\n```\n\n### Creating a store\n\nA new store is created with the createStore function:\n```typescript\nimport { createStore } from \"forimmer\";\n\nconst store = createStore\u003c{\n  foo: string;\n  bar: string;\n}\u003e();\n```\nYou can also supply an initial state:\n```typescript\nimport { createStore } from \"forimmer\";\n\nconst store = createStore\u003c{\n  foo: string;\n  bar: string;\n}\u003e({foo: \"foo\"});\n```\n\nIf your initial state covers the entire desired State interface, you may omit the generics and createStore will infer the type of your state:\n```typescript\nimport { createStore } from \"forimmer\";\n\nconst store = createStore({foo: \"foo\", bar: \"bar\"});\n```\n\n### Using the store state in your react app:\n\nIf you just want to access the current state of the store, use:\n```\nconst state = store.getCurrentState();\n```\n\nThe useStoreState hooks allows you to pick values from the store (returning them in an array) and subscribe to changes thereof:\n```typescript\nfunction SomeComponent() {\n  const [foo] = useStoreState(store, state =\u003e [state.foo]); // useStoreState will infer the type of foo\n  return \u003cdiv\u003e{foo}\u003c/div\u003e\n}\n```\nWhenever state.foo changes (and not any other value), the component will re-render and foo will be updated to the new value.\n\n### Suspense integration\n\nIf any of the values returned from useStoreState is undefined OR if trying to access a value deeper in the state tree would throw an error, useStoreState will throw a Promise which resolves once the store has been updated and the desired values become available.\n\nYou can wrap your component in React.Suspense to show a fallback UI that will automatically appear when useStoreState throws and the loading Promise is pending:\n```typescript\nfunction ComponentWithSuspense() {\n  return \u003cReact.Suspense fallback={\u003cdiv\u003e...loading foo\u003c/div\u003e}\u003e\n    \u003cSomeComponent /\u003e\n  \u003c/React.Suspense\u003e\n}\n```\n\n### Modifying the store state with actions\n\nIn order to modify the store state, you can define store actions. A store action is an async function (that might for instance fetch data from an API) which returns a state recipe, which in turn is a function which is passed a draft state to modify (see the immer docs):\n```typescript\n\n// this will create a store action with a payload:\nconst setFoo = store.createStoreAction(\n  async (newFoo: string) =\u003e draft =\u003e {\n    draft.foo = newFoo;\n  }\n);\n\nsetFoo(\"foo\") // set store.foo to \"foo\"\n\nconst fetchFoo = store.createStoreAction(\n  async () =\u003e {\n    const foo:string = await someApiCall();\n    return draft =\u003e {\n      draft.foo = newFoo;\n    }\n  }\n);\nfetchFoo() // fetch foo from some api\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichael-klein%2Fforimmer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmichael-klein%2Fforimmer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichael-klein%2Fforimmer/lists"}