{"id":18347630,"url":"https://github.com/ghivert/react-purestore","last_synced_at":"2026-04-25T12:37:07.677Z","repository":{"id":57342940,"uuid":"239179372","full_name":"ghivert/react-purestore","owner":"ghivert","description":"State and Side Effects management in React, using Hooks. Favor pure functional programming to simplify code.","archived":false,"fork":false,"pushed_at":"2020-06-16T14:19:41.000Z","size":414,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-15T15:36:49.086Z","etag":null,"topics":["dispatch","effects","react","side-effects","state-management","store"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/ghivert.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":"2020-02-08T18:06:57.000Z","updated_at":"2020-06-16T14:19:22.000Z","dependencies_parsed_at":"2022-09-16T02:50:29.625Z","dependency_job_id":null,"html_url":"https://github.com/ghivert/react-purestore","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghivert%2Freact-purestore","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghivert%2Freact-purestore/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghivert%2Freact-purestore/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghivert%2Freact-purestore/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ghivert","download_url":"https://codeload.github.com/ghivert/react-purestore/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248127830,"owners_count":21052296,"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":["dispatch","effects","react","side-effects","state-management","store"],"created_at":"2024-11-05T21:14:33.299Z","updated_at":"2026-04-25T12:37:02.653Z","avatar_url":"https://github.com/ghivert.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# React PureStore\n\nHooks in React are a little revolution. You can write your pure functions without worrying about the states and components. Everything is components. Everything can be turned into stateful component. But why not everything could be store connected as easily? That’s one of the points that PureStore tries to address.\n\nWhat could be simpler than `useStore`? First, write your component. Then, use `useStore`. You’re done and connected to the store. Only your connected components will repaint on store update.\n\n```jsx\nimport React from 'react'\nimport { useStore } from 'react-purestore'\n\nconst Counter = () =\u003e {\n  const store = useStore() // Connects to the store!\n\n  // Below the classic counter!\n  const [state, setState] = useState(0)\n  return (\n    \u003cdiv className=\"counter\"\u003e\n      \u003cbutton onClick={() =\u003e setState(state + 1)}\u003e+\u003c/button\u003e\n      \u003cdiv\u003e{state}\u003c/value\u003e\n      \u003cbutton onClick={() =\u003e setState(state - 1)}\u003e-\u003c/button\u003e\n    \u003c/div\u003e\n  )\n}\n```\n\n# The Store\n\nThe store is implemented as pure JavaScript, without any framework. It is compound of two parts: `state` and `dispatch`.\n\n`state` is the content of the store when you ask for data. Just like in above example, `state` contains the value stored into the store. If you want to get your data, get the store, and access state.\n\n`dispatch` is the main function of the store. It accepts a function name and potentially a payload, and run the store-function  against the latest state and the payload. It works like a combination of Redux and Vuex.\n\nLet’s take an example with the above code: let’s add an increment and decrement handler in the component.\n\n```jsx\nimport React from 'react'\nimport { useStore } from 'react-purestore'\n\nconst Counter = () =\u003e {\n  const { state, dispatch } = useStore()\n  return (\n    \u003cdiv className=\"counter\"\u003e\n      \u003cbutton onClick={() =\u003e dispatch('increment')}\u003e+\u003c/button\u003e\n      \u003cdiv\u003e{state}\u003c/value\u003e\n      \u003cbutton onClick={() =\u003e dispatch('decrement')}\u003e-\u003c/button\u003e\n    \u003c/div\u003e\n  )\n}\n```\n\n## Creating a store and defining handlers\n\n\u003e So you can use a store and call handlers. But, wait a minute, how do you define handlers? If I’m calling `increment` and `decrement` like this, I just get an error!\n\nThat’s correct. When calling a handler via `dispatch`, it calls the corresponding handler defined in the store. And in our examples, we never defined a store! So let’s defining a store. A store is made up of three parts: an initial state, an object of `actions`, and an object of `mutations`. The initial state is how the store should be initialized the first time. The `actions` and `mutations` are extremely similar, but let’s start only with `mutations`.\n\nAn initial state could be what you want, but a `mutation` is a function accepting a state as first parameter, and potentially a payload, and returning the new state. The new state will take place of the old state. It’s up to you to take care to not break things. So, for our early example `increment`, we could write the function like this:\n\n```javascript\nconst increment = state =\u003e {\n  return state + 1\n}\n```\n\nAnd similarly for `decrement`:\n\n```javascript\nconst decrement = state =\u003e {\n  return state - 1\n}\n```\n\nThat’s cool, but how could we wire up all of this? Let’s use the `Store` contructor!\n\n```javascript\n// store.js\nimport { Store } from 'react-purestore'\n\nconst initialState = 0\n\nconst mutations = {\n  increment,\n  decrement,\n}\n\nconst store = new Store(initialState, mutations)\n\n// Here, store is a Store object containing state and dispatch.\nexport default store\n```\n\nNow we can call `store.dispatch('increment')` and `store.dispatch('decrement')` whenever we want because we have access to the `store`!  \nAll we have to do now is to connect the store into `React` to get our store widely available. And for this, we got a `Provider`.\n\n```jsx\nimport React from 'react'\nimport * as PureStore from 'react-purestore'\nimport store from './store'\n\nconst App = () =\u003e (\n  \u003cPureStore.Provider store={store}\u003e\n    {/* Everything in there could call useStore whenever needed to\n      connect to the store. */}\n    \u003cCounter /\u003e\n  \u003c/PureStore.Provider\u003e\n)\n```\n\nNow, everytime you need to call the store, just use `useStore` in your components and you get connected!\n\n## What about side effects?\n\nUntil then, if you have been attentive, we never talked about asynchronous programming. And for a reason: side effects are complicated and error-prone in an application written in a functional style. Indeed, if you take a closer look at your React components, none of them contains side effects: the store take care of them for you. You can just focus on writing your code, your functions and your components without thinking about wiring this up. But someday you have to make some asynchronous tasks, and all of your beautiful model will be destroyed. But fortunately, you don’t need to worry about them, because the store can handle them for you directly, and in global manner, avoiding you to think about `useEffect`. Let’s introduce `actions`!\n\n## Actions\n\nActions are functions you define in the store, at the creation. They behave exactly like mutations, but they return an object instead of a state. This object contains up to two fields: a combination of `state`, `effect` and `effects`. `state` should contain the new state, just like a mutation. The different point is for `effect` and `effects`. They both contains side effects functions, described, ready to be run. The effects are `Effect`, a class which is part of PureStore.\n\nAn `Effect` is a little bit like a `Promise`, in the sense that it is an object that will contain asynchronous behavior and which will be resolved later. It is made of two things: a bunch of messages (success and failure) and an asynchronous `run` function which should return a `Promise`.\n\nWhen you give an effect to the runtime as an action return value, this effect will be resolved by the runtime, and it will ping the store with the correct message according to the `Promise` result (success for a successful promise, failure for a failed promise). Let’s take an example with an HTTP request.\n\n```javascript\nimport { Effect } from 'react-purestore'\n\n// First we define the HTTP Effect.\nconst http = ({ success, failure, url, json, method }) =\u003e {\n  return new Effect({ success, failure }, async () =\u003e {\n    const response = await fetch('url', { method: method || 'GET' })\n    if (json) {\n      return response.json()\n    } else {\n      return response.text()\n    }\n  })\n}\n\n// Then we define the store with the actions.\nconst initialState = {\n  content: null,\n  error: null,\n}\n\nconst mutations = {\n  // Here, we will process the response of the request if the request passed.\n  treatResponse(state, content) {\n    return { ...state, content }\n  },\n  // Here, we will process the response of the request if it failed.\n  showFailure(state, error) {\n    return { ...state, error }\n  },\n}\n\nconst actions = {\n  // In this function, all we’ll do is giving the side effect to runtime.\n  //   Note that the Effect is not running for now. We defined the Effect earlier,\n  //   But this will be run only when the runtime need to get response.\n  doHTTPRequest(state) {\n    const effect = http({\n      success: 'treatResponse',\n      failure: 'showFailure',\n      url: '/api/content',\n      method: 'GET',\n    })\n    // We won't change the state here, but we will run the http effect.\n    return { state, effect }\n  },\n}\n\nconst store = new Store(initialState, mutations, actions)\n```\n\nWe can see all the different steps: the `http` function define the Effect, and define the `run` function. It is the line `new Effect({ success, failure }, async () =\u003e {})`. You can think of it a little like a promise: the first options are the message functions-name that will be called when the run function resolves, and the second is the run function. The structure is **always the same**.\n\nYou can define nearly all side effects you need: you just need to define them with a `new Effect`. And don’t worry, Effect is defined like an ES6 class, so calling it without `new` will just throw an Error!\n\n# Recap\n\nPhew, that was huge! Now you have everything you need to get started with you first app! I would love to hear about you if you like it and improve it! Of course, contributions are more than welcome, and like all of my work, I try to do as much as possible publicly.\n\n## Advanced tips\n\nIf you’re here, you’re probably used to the framework. Nice! Here are some tips to help you design your applications.\n\n## Effects\n\n\u003e Having `Effect`s is cool, but why are they here? I mean, you can mimic the behavior of effects with just simple Promise.\n\nAnd you’re right. But `Effect`s are a little more complex. They are composable. To do so, they expose different functions: `map`, `then` and `Effect.all`. First things first, `map` and `then`. `map` is a function allowing to transform the result of Effects before they will be returned to the store. `then` allow to chain Effects in order to change context after an Effect has been run.\n\n```javascript\n// We’ll keep the http effect defined earlier.\nconst jsonResult = http({\n  success: 'httpResponse',\n  failure: 'httpFailure',\n  url: '/api/content',\n  method: 'GET',\n  json: true,\n})\n\n// Let’s say extractIdFromPage is defined and returns an ID.\nconst idResult = jsonResult.map(content =\u003e extractIdFromPage(content))\nconst otherThing = idResult.then(content =\u003e http({\n  success: 'secondHttpResponse',\n  failure: 'secondHttpFailure',\n  url: `/api/post/${content}`,\n  method: 'GET',\n}))\n```\n\nBe careful, you cannot return an Effect in a map, and you should return an `Efffect` in then. Of course any failure in the chain will automatically stop the chain.\n\nFinally all, the composability function. It behaves a little like `Promise.all`.\n\n```javascript\nconst firstEffect = http({\n  success: 'firstSuccess',\n  failure: 'firstFailure',\n  url: '/api/content',\n})\n\nconst secondEffect = http({\n  success: 'secondSuccess',\n  failure: 'secondFailure',\n  url: '/api/content',\n})\n\nconst options = { success: 'allSuccess', failure: 'allFailure' }\n\nconst alls = Effect.all(options, [firstEffect, secondEffect])\n// Once ran, the store will be called with allSuccess with the payload equal to\n//   { firstSuccess: [result], secondSuccess: [result] } or with allFailure with\n//   an object similar to success but with messages of failed Effects.\n```\n\n## Custom Hook gets\n\nWhat’s cool about hook is that you can easily define yours. And that’s the same with PureStore. Let’s see it with `useStore`. Imagine your store is defined like this:\n\n```javascript\nconst initialState = {\n  users: [{\n    id: '6b393624-fbe0-4318-a2b5-3240874a683d',\n    name: 'Alan Turing'\n  }],\n  posts: [{\n    title: 'A Turing Machine is great!',\n    content: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',\n    id: '56a5b3d1-6386-4d99-ac72-01a3990b89da',\n    authorId: '6b393624-fbe0-4318-a2b5-3240874a683d'\n  }],\n}\n```\n\nA store like this is great because you have only one source of truth in your application. If something refers to the users, they always will refer to their id, nothing else. No data duplication, nothing more than an id. That’s cool, but what if you have a render like this:\n\n```jsx\nconst Post = ({ title, content, name }) =\u003e (\n  \u003cdiv className=\"post\"\u003e\n    \u003ch2 className=\"title\"\u003e{title}\u003c/h2\u003e\n    \u003cdiv className=\"author\"\u003e{name}\u003c/div\u003e\n    \u003cdiv className=\"content\"\u003e{content}\u003c/div\u003e\n  \u003c/div\u003e\n)\n```\n\nYou would probably write something like this:\n\n```jsx\nconst AllPosts = () =\u003e {\n  const { state } = useStore()\n  const { posts, users } = state\n  return posts.map(({ id, authorId, title, content }) =\u003e {\n    const { name } = users.find(user =\u003e user.id === authorId)\n    return (\n      \u003cPost\n        key={id}\n        title={title}\n        content={content}\n        name={name}\n      /\u003e\n    )\n  })\n}\n```\n\nThat’s rather cool, but could we make it simpler? Because, if we’re reusing the posts regularly, we will be forced to refind the user each time. We can clearly make it better. With a custom hook.\n\n```jsx\nconst usePosts = () =\u003e {\n  const { state } = useStore()\n  const { posts, users } = state\n  return posts.map(({ id, authorId, title, content }) =\u003e {\n    const { name } = users.find(user =\u003e user.id === authorId)\n    return { id, authorId, title, content, name }\n  })\n}\n\nconst AllPosts = () =\u003e {\n  const posts = usePosts()\n  return posts.map(({ id, ...post }) =\u003e (\n    \u003cPost key={id} {...post} /\u003e\n  ))\n}\n```\n\nPhew, that removed a lot of code in the `AllPosts` function! And it’s cool, because `usePosts` is reusable anywhere you want, with all the data perfectly formed. No more problems with fetching the correct data from different fields. Just call the hook and you’re good to go. And you know what’s even cooler? You can nest the hooks!\n\nTo illustrate it, imagine the store is improved:\n\n```javascript\nconst initialState = {\n  companies: [{\n    name: 'FrenchPastries',\n    id: '2aef849f-afd3-4ca2-b843-627e3fcc57d2',\n  }],\n  users: [{\n    id: '6b393624-fbe0-4318-a2b5-3240874a683d',\n    name: 'Alan Turing',\n    companyId: '2aef849f-afd3-4ca2-b843-627e3fcc57d2',\n  }],\n  posts: [{\n    title: 'A Turing Machine is great!',\n    content: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',\n    id: '56a5b3d1-6386-4d99-ac72-01a3990b89da',\n    authorId: '6b393624-fbe0-4318-a2b5-3240874a683d'\n  }],\n}\n```\n\nNow imagine we want to get the final object Post defined like:\n\n```javascript\nconst Post = {\n  title: String,\n  content: String,\n  id: String,\n  user: {\n    id: String,\n    name: String,\n    company: {\n      name: String,\n      id: String,\n    }\n  }\n}\n```\n\nTo ease the call, we can nest the hooks!\n\n```jsx\nconst useCompanyUsers = () =\u003e {\n  const { state } = useStore()\n  const { companies, users } = state\n  return users.map(({ companyId, ...user }) =\u003e {\n    const company = companies.find(company =\u003e company.id === companyId)\n    return { ...user, company }\n  })\n}\n\nconst usePosts = () =\u003e {\n  const { state } = useStore()\n  const { posts } = state\n  const users = useCompanyUsers()\n  return posts.map(({ authorId, ...post }) =\u003e {\n    const user = users.find(user =\u003e user.id === authorId)\n    return { ...post, user }\n  })\n}\n```\n\nHere, when calling `usePosts`, we’ll directly have the correct posts, correctly formed. Hooray!\n\n## Defining subscriptions\n\nWhat a subscription is? It’s when your side effects is repeted often times. When you’re making an HTTP request, the request will be resolved once, and it’s over. But what about a `setInterval`? Or a WebSocket subscription? A message can come any time. To be able to put them into the Store correctly, we need to make it through to the `mutations` or the `actions` functions. We can do it easily once we have defined the store:\n\n```javascript\nimport store from './store'\nimport ws from 'websocket'\n\nconst App = () =\u003e (...)\n\n// This will ping the store every second.\nsetInterval(() =\u003e {\n  store.dispatch('ping')\n}, 1000)\n\n// Every times a message is received from WS, ping the correct action or mutation\n//   in the store with the message.\nws.connect('my.api', message =\u003e {\n  store.dispatch('receivedWSMessage', message)\n})\n```\n\n## Nesting action\n\nIf your store begins to be too big, it’s probably time to split it up, in order to reduce the mental overhead and to ease a lot of things, like team work. You can easily split the actions and mutations by nesting them.\n\n```javascript\nconst initialState = { todos: [] }\n\n// Nested actions.\nconst mutations = {\n  todos: {\n    add(state, todo) {\n      return { ...state, todos: [...state.todos, todo] }\n    }\n  }\n}\n\n// Creates the store.\nconst store = new Store(initialState, mutations)\n\n// And now we dispatch the action!\nstore.dispatch('todos.add', 'Think to split the store!')\n```\n\nAs you can see, when dispatching `message1.message2...messageN`, it will try to lookup inside of the actions each time. For now, the state is not automatically opened to provide only the namespace, but is rather given entirely. This allow to properly open namespace the action, but still be able to work on the entire state. Open an issue if you think we should do differently.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fghivert%2Freact-purestore","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fghivert%2Freact-purestore","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fghivert%2Freact-purestore/lists"}