{"id":21487121,"url":"https://github.com/dtinth/redux-waitfor","last_synced_at":"2025-07-15T15:30:42.035Z","repository":{"id":57156673,"uuid":"47932479","full_name":"dtinth/redux-waitfor","owner":"dtinth","description":"Reducer combinator that allows reducers to wait upon each other.","archived":false,"fork":false,"pushed_at":"2016-01-31T10:54:30.000Z","size":11,"stargazers_count":21,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-09-21T16:18:39.402Z","etag":null,"topics":["redux"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/dtinth.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":"2015-12-13T19:10:02.000Z","updated_at":"2021-09-16T21:29:39.000Z","dependencies_parsed_at":"2022-08-30T03:21:05.216Z","dependency_job_id":null,"html_url":"https://github.com/dtinth/redux-waitfor","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/dtinth%2Fredux-waitfor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dtinth%2Fredux-waitfor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dtinth%2Fredux-waitfor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dtinth%2Fredux-waitfor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dtinth","download_url":"https://codeload.github.com/dtinth/redux-waitfor/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226047614,"owners_count":17565375,"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":["redux"],"created_at":"2024-11-23T13:26:42.638Z","updated_at":"2024-11-23T13:26:43.301Z","avatar_url":"https://github.com/dtinth.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\nredux-waitfor\n=============\n\nReducer combinator that allows reducers to wait upon each other.\n\n\nWarning\n-------\n\n__You might not need to use this.__\nSee this discussion: [waitFor leads to wrong design](https://github.com/facebook/flux/issues/209).\nAlso see this discussion about [reducers depending upon each other on Redux](https://gist.github.com/gaearon/d77ca812015c0356654f#gistcomment-1466314).\n\nIn the motivation section, I’ve explained an alternative without using `waitFor`,\nbut since I’ve alread spent time creating, testing, and documenting this thing,\nI’ll put it online anyway.\n\n\nMotivation\n----------\n\nWhen building a Redux app, there are some cases that a reducer does not only\ndepend on the `state` and `action`, but also depend on the state returned by\nanother reducer.\n\nFor instance, I am building an expense tracking application using speech\nrecognition. Therefore, there are these events:\n\n- `SpeechRecognitionInitialize` (fired when tapping the mic)\n- `SpeechRecognitionStart` (fired when the app is ready to listen)\n- `SpeechRecognitionResult` (fired as you speak)\n- `SpeechRecognitionEnd` (fired when speech recognition ended)\n\nMy `transcript` reducer consumes these series of events (actions) and produces\nthe transcript of what I just said (e.g. “40 Baht food”).\n\nFrom the `transcript`, I need to derive an `interpretation` from the spoken text\n(e.g. `{ amount: 40, category: \"food\" }`).\n\nFrom the `interpretation`, I need to derive an expense entry which will be saved\ninto the database.\nHowever, before I save it to the database, I want to be able to edit it before I save.\n\nI came up with two choices to architect this:\n\n1. Put `transcript`, `interpretation`, and `stagedDatabaseEntry` into the store.\n   This is the first, most obvious idea that comes into my mind.\n   If I use Flux, I’d do it this way and it’d be perfectly fine.\n   _“This is clearly the way,”_ I said to myself.\n\n   But this means we need mechanisms to set up dependencies between these reducers,\n   so that when speech is recognized, the `interpretation` reducer has access\n   to the latest `transcript`, and `stagedDatabaseEntry` has access to the\n   latest `interpretation`.\n\n   This is where `redux-waitfor` comes into play.\n\n   Think of this approach as using materialized views.\n   Also consider the next option, as this option might not be the most appropriate one.\n\n2. Only put `transcript` in the store, and use [reselect](https://github.com/rackt/reselect)\n   to derive both `interpretation` and `stagedDatabaseEntry`.\n\n   For the last requirement that I want to modify the `stagedDatabaseEntry` before\n   actually saving it to the database, I’ll just store the `stagedDatabaseOverrides`\n   instead and derive the actual entry on-the-fly.\n\n   This solution is less obvious to me, and I only came up with it as I write\n   the documentation for this `redux-waitfor` package.\n\n   Think of this approach as using a (non-materialized) view of the database.\n   In fact, this may be a better option.\n   The data in the store is normalized, which means no dependencies or need to synchronize.\n   No magic `waitFor` tricks, which leads to simpler code.\n\n\nUsage\n-----\n\nIf you insist on using this thing, first, you need to import it:\n\n```js\nimport { waitFor, combineReducers } from 'redux-waitfor'\n```\n\n### waitFor(key, inject)\n\nThis is a function that takes a reducer, and returns a reducer that waits for\ndata from another part of the store with the specified `key`.\n\n```js\nconst interpretationReducer = waitFor('transcript', transcript =\u003e\n  (state = { }, action) =\u003e deriveInterpretationFromTranscript(transcript)\n)\n```\n\nBut wait! How can a reducer wait for more data? That seems impossible.\nLet’s try invoking this magical reducer.\n\n```\n\u003e interpretationReducer(void 0, action)\n[Function]\n```\n\nA function (a thunk) is returned instead of the new state!\nThis signifies that we need more data from other parts of the store.\nWe then pass the state from other parts of the store into that thunk:\n\n```\n\u003e interpretationReducer(void 0, action)({\n    transcript: '40 Bath food'\n  })\n{ amount: 40, category: 'Food' }\n```\n\nNow we have the actual, new state.\n\nWhat really happens is that when we invoke that thunk, it will inject\nthe state of the thing it’s waiting for into the `inject` function\n(specified as a parameter to `waitFor`).\nThat `inject` function then takes it and returns a reducer,\nwhich is then immediately invoked.\n\n\n### combineReducers(reducers)\n\nThis is Redux’s `combineReducers`, but works with thunks.\n\n__How it works:__ Perhaps the easiest way to explain it is by examples.\nHere is our current state:\n\n```js\n{ transcript: '',\n  interpretation: null,\n  stagedDatabaseEntry: null }\n```\n\nAn action happened. It is dispatched to each reducer, just like Redux’s `combineReducers`.\n\nNow, some reducer returned the new state, and some returned a thunk:\n\n```js\n{ transcript: '40 Baht food',\n  interpretation: [Function],\n  stagedDatabaseEntry: [Function] }\n```\n\nWe then enter the __digest cycle__. We send the above state into each thunk.\n\nSince `transcript` is available, the thunk injects it and invokes the reducer.\nMeanwhile, the `interpretation` is not yet available during that digest cycle.\nIn this case, the thunk returns itself.\n\nThis is the resulting state:\n\n```js\n{ transcript: '40 Baht food',\n  interpretation: { amount: 40, category: 'food' },\n  stagedDatabaseEntry: [Function] }\n```\n\nThis means we need another digest cycle.\nWe did the same, and obtain the final state:\n\n```js\n{ transcript: '40 Baht food',\n  interpretation: { amount: 40, category: 'food' },\n  stagedDatabaseEntry: { /* ... */ } }\n```\n\nFor more example and tests, see [example.js](example.js) (which also serves as a unit test — it’s pretty comprehensive!).\n\nAs you can see, this is quite advanced and requires lots of explanation.\nPerhaps you can take this advice from The Zen of Python instead:\n\n\u003e __If the implementation is hard to explain, it's a bad idea.__\u003cbr /\u003e\n\u003e If the implementation is easy to explain, it may be a good idea.\n\nSo, unless you really need to, don’t use this library!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdtinth%2Fredux-waitfor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdtinth%2Fredux-waitfor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdtinth%2Fredux-waitfor/lists"}