{"_id":"redux-rags","_rev":"33-1a6a9c50c8a63a950fa394371addac47","name":"redux-rags","dist-tags":{"beta":"1.2.1-beta3","latest":"1.2.3"},"versions":{"0.0.1":{"name":"redux-rags","version":"0.0.1","keywords":["redux"],"author":{"name":"CodeSignal"},"license":"MIT","_id":"redux-rags@0.0.1","maintainers":[{"name":"kyleatcodesignal","email":"kyle@codesignal.com"}],"homepage":"https://github.com/CodeSignal/redux-rags#readme","bugs":{"url":"https://github.com/CodeSignal/redux-rags/issues"},"dist":{"shasum":"173fe6fc6834d9765e1a462b32a0e3d633e0bf39","tarball":"https://registry.npmjs.org/redux-rags/-/redux-rags-0.0.1.tgz","fileCount":10,"integrity":"sha512-x5cTF3kx1unE4lHS+Tq8exKAXVojZ6HdbigTQyoPIiL7d1uMcqN1x/+CF4cQb5cqDniB/iEZ4ekui152SZ9SRg==","signatures":[{"sig":"MEUCIQCyM0/qypHEq/tvi7F1zUNfjZOVgmmmR7qdGeExJHX/SAIgTGDa8d8lQfBVwCyhJCigPEJtJq2zOXJsxGu2JF0Sqkw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":39229,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb6cL2CRA9TVsSAnZWagAABIoP/1So5RCXs8QHOVbVVhOV\nc4+E2+2TFoNWOkzE2BCDKfSRDITjDEwwrdzJtrGth6da+OeynuKSVhmU2G8y\n5aBu6AP+++1V3Y6Ya4GGvCJIrxtkeS/lO7R15rYbTh33PHa1V3REL0GesN3U\nGkrKXBZy7wOwAaJKntm2AjbnhrS5gym2dX4GTEcTsXA06selHM9rvy0isMrr\nKdQHdlZ3EKXrTp1Y7C4gGFci2xMT2Y3tKOpB1DCLDx+p+ZL9VTkHTveYHd1+\ncb2cY9nzNHOQUQBnZMxbeLYssDhUu4TpKoOQQFeBl7/IeCdZZwgQsbD5nH/s\nclBvADBdomsr4wcahbURR9V8wWTWnP4ZN3XcpC9LJFDccp4PGu9eFsmufL2Y\nB22/rYdjWROPrqPe2C0pVfv+jjF8pch263RBj2TA1qhGc/HMSIQxhlvW0WA6\nxWBRQHnRSEaQmH3u5875xYfFBJZR/z4Jq9hAd4AUSW2JBN5vg3MSdNZqpTYJ\nX5vfH9wE3IMfCv51/ifp2P7/+4VQZZ9BdYFYP4dG8VU3H627DEIH+5zU6+jU\npoINdPxDaUIpjO6ya9zt8PXmjpLHMi6VuuuWRtuD8zk1n6CcDWBriO13yryK\njSvuuC9TIEDBG0JXISpm3l8t0o0Keq7lPsezCYqyvMFtsioHn7JVOtq0rbCA\nLu94\r\n=0rsH\r\n-----END PGP SIGNATURE-----\r\n"},"jest":{"testMatch":["<rootDir>/**/?(*.)+(test).js"],"transform":{"^.+\\.js$":"<rootDir>/test/jest.transform.js"},"modulePaths":["<rootDir>/"],"moduleFileExtensions":["js"]},"main":"index.js","gitHead":"dc57dea52ebc2fe28aefa32362dfb956490af3bb","scripts":{"flow":"flow","jest":"jest --maxWorkers=2","lint":"eslint src/**","test":"npm run jest","build":"babel src -d build --copy-files","jest:watch":"npm run jest -- --watchAll","lint:watch":"esw -w lib/**","prepublish":"npm run build"},"_npmUser":{"name":"kyleatcodesignal","email":"kyle@codesignal.com"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.","repository":{"url":"git+https://github.com/CodeSignal/redux-rags.git","type":"git"},"_npmVersion":"5.6.0","description":"Redux Reducers, Actions, and Getters. Simplified!","directories":{},"_nodeVersion":"8.9.4","_hasShrinkwrap":false,"devDependencies":{"jest":"^23.6.0","redux":"^4.0.1","eslint":"^5.8.0","flow-bin":"^0.86.0","babel-cli":"^6.26.0","babel-core":"^6.26.3","babel-jest":"^23.6.0","babel-eslint":"^10.0.1","eslint-watch":"^4.0.2","babel-preset-env":"^1.7.0","babel-preset-flow":"^6.23.0","babel-preset-es2015":"^6.24.1","babel-preset-stage-3":"^6.24.1","eslint-plugin-import":"^2.14.0","babel-plugin-transform-runtime":"^6.23.0","babel-plugin-dynamic-import-node":"^2.2.0","babel-plugin-syntax-dynamic-import":"^6.18.0","babel-plugin-transform-class-properties":"^6.24.1"},"_npmOperationalInternal":{"tmp":"tmp/redux-rags_0.0.1_1542046453885_0.8266148733413172","host":"s3://npm-registry-packages"}},"0.1.0":{"name":"redux-rags","version":"0.1.0","keywords":["redux"],"author":{"name":"CodeSignal"},"license":"MIT","_id":"redux-rags@0.1.0","maintainers":[{"name":"kyleatcodesignal","email":"kyle@codesignal.com"}],"homepage":"https://github.com/CodeSignal/redux-rags#readme","bugs":{"url":"https://github.com/CodeSignal/redux-rags/issues"},"dist":{"shasum":"e09dae077fb6abaa2db44263155946d787553ff7","tarball":"https://registry.npmjs.org/redux-rags/-/redux-rags-0.1.0.tgz","fileCount":10,"integrity":"sha512-pwf4TgKqtV2NsfBBnwOJcQCU7Yz6eE73waHauBHjhD3WbLRJ0Q1wYSzz+ZJfXDDB7nqxofurLKoWtWsVUuwyaA==","signatures":[{"sig":"MEUCIHAMH+wHM1YTO3vrtk1ie6De2pCp3DSY+nEImXF6aDDeAiEAzIIHjtkSs9Hrhap+N0f6QQKTtFE/+p1XgggxYshM+eQ=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":39259,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb6cWpCRA9TVsSAnZWagAA9F8P/A8wrcBJX4XkXUI0lojG\nlqbJ7SEPgwLEwDc+ZpMR9kz061+0kxwjF1I8QdNpHmSDg4j77voaY+G8Gbw9\n6sqQ1+bNmwoJi/Ou/jjJ4AM3sSrRROd0tq7k8TK2AjjrME1b1C8ZqARKZcEK\ngPOiWCE6KyoYVBrkNk+YKf1k/Pa2eXOZmRwiuTzUhpnu0vxX8tI4zUZG6uvU\nLyy49qZglB7AVJxzQzzUvlLu7x0/l7F+nlGDHwh6U9iQU4+z96PKmmtSqsy5\njuVuIGA88YxrOo0AXIhdnJqTlHDuLooupm3QsxuUCWtdgM5TWMVM9VLwY7E8\n1RPZa5CIEshxB4d1AjXSUt+QtpoxAeDVTcTVsLR2McG48Zntg6xhkEDjRbiu\n136U4lxIhFRain3rSCx7sr2hxosueZ3tpMCKm+Jhp4rS6dYnU2+hXbv4mept\nx/BhYZQ0tLBXVzDhZpOdEyS7+1y/zIrMGosd79GIc01X3WunnYOj23WQD5TK\nsXp6MG1KSLVdCiLXDlt3S2tTevaT3t0PHnqQqzuQyscIZiXMx4kpht9SaGxx\nV5Pp0s1QlJIwK8f1XoWlXfStFN/I0QsmHzDX1wU6hFN/MEsb5rzTXQC6LQzm\nm/7cxDwbV62zIXr99WQSoGp2hz+BecFZ6juOWfTwsdfqP07fgYJeQhfbbNWe\nnY8B\r\n=lZIO\r\n-----END PGP SIGNATURE-----\r\n"},"jest":{"testMatch":["<rootDir>/**/?(*.)+(test).js"],"transform":{"^.+\\.js$":"<rootDir>/test/jest.transform.js"},"modulePaths":["<rootDir>/"],"moduleFileExtensions":["js"]},"main":"index.js","readme":"# Redux-Rags\nRedux Reducers, Actions, and Generators: Simplified!\n\nGenerate and automatically inject reducers that manage the `begin load -> load -> endLoad -> set data || set error` lifecycle for data fetching.\n\nTLDR: `({ load }) => ({ actions: { load }, getters: { getData } })`\n\n## Motivation\nWe found that a lot of our reducers were redefining a common theme. We have some endpoint\nthat we want to query for data. So we'll want a `begin loading` action, so we can render\na fancy loading spinner. Then we'll hit the endpoint, maybe it returns data, maybe we get\nan error. But we definitely stopped loading. And that's it. A mini-state machine that we\nredefined dozens of times. But what if there were an easier way? What if we could define\nthat mini state machine once and re-use the logic with each query? Well `redux-rags` is\nhere to help clean up all the spilled copy-pasta from your redux boilerplate!\n\nHuge thanks to Jimmy Shen for [this amazing Medium article](https://medium.com/@jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7)\non dynamically injecting reducers into the redux store.\n\n## Usage\n\n### Simplest Example\nHere we want to hit an endpoint with the `userId` parameter. We'll avoid worrying about loading states or anything for now. The call to `ragFactory` creates the actions and injects the subreducer for us. Then this state information is stored in redux, so when users return to the component they'll see the cached data. \n\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { ragFactory } from 'redux-rags';\n\n// Pass load to factory, factory returns a load that we can give to redux, will have the same signature as the function we passed in.\nconst { actions: { load }, getters: { getData } } = ragFactory(\n  load: (userId) => axios.get('/users', { userId })\n});\n\nfunction ViewUser({userId, load, user}) {\n  return (\n    <div>\n      <button onClick={() => load(userId)}>Load User Info</button>\n      {user && <UserData data={user} />}\n    </div>\n  )\n}\n\nexport default connect(state => ({ user: getData(state)}), { load })(ViewUser)\n```\n\n### Using the `ragFactory` in another file\nHere's how you'd interact with redux for a data request. The returned `load` function will take the same arguments as the `load` function passed in.\n```js\nimport { ragFactory } from 'redux-rags';\n\nconst { actions: { load }, getters: { getData, getIsLoading } } =\n  ragFactory({ load: () => axios.get('/faq') });\n\nexport {\n  loadFaq: load,\n  getFaq: getData,\n  getIsFaqLoading: getIsLoading\n};\n```\n\n#### Use that Data Request in a React Component\nImport the Getters and Action as you normally would and use them with `react-redux`.\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { loadFaq, getFaq, getIsFaqLoading } from './faqData';\nimport Loading from './Loading';\nimport FaqItem from './FaqItem';\n\nclass Faq extends React.Component {\n  componentDidMount() {\n    this.props.loadFaq();\n  }\n\n  render() {\n    const { isLoading, faqData } = this.props;\n    if (isLoading) {\n      return <Loading />;\n    }\n\n    return (\n      <React.Fragment>\n        {faqData.map((data, index) => <FaqItem key={index} data={data} />}\n      </React.Fragment>\n    )\n  }\n}\n\nconst mapStateFromProps = state => ({\n  faqData: getFaq(state),\n  isLoading: getIsFaqLoading(state)\n});\n\nexport default connect(mapStateFromProps, { loadFaq })(Faq);\n```\n\n### Placing the subreducer in a custom location in redux\nConnect subreducer to your desired location in the redux store, and tell the generator\nwhere you put it. Getters are also factories for you. If you don't care where the\nsubreducer lives, don't pass in a `getInStore` method and the generator will place it for you.\nIn this case taking `subreducer` and adding it to the `combineSubreducers` function in the `currentUser` subreducer. (But you can place\nit wherever you want, just update the `getInStore` function passed to `ragFactory`.\n```js\nimport { generator };\nconst { actions, subreducer, getters } = generator({\n  name: 'MY_DATA',\n  load: (param) => methodService.run('fetchSomething', param),\n  getInStore: (store) => store && store.currentUser && store.currentUser.my_data\n});\n```\n\n### Using update to manipulate the stored value\nIn addition to `load` you can also pass in an `update` function, which will take the current `data` value as the first parameter. An example of this might be an update function that increments the current value by 2. The signature of the returned function is slightly different, as the `data` first argument is passed internally, so you pass in a function that takes parameters `(currentData, ...additionalParams)` and the returned `actions.update` function will take the parameters `(...additionalParams)`.\n```js\nimport { generator };\nconst { actions : { update }, getters } = generator({\n  name: 'MY_DATA',\n  update: (currentData) => currentData + 2,\n});\n```\n\n### Using the ragFactoryMap\nNot much to say, it's pretty much how you would use `ragFactory`, except treating the input arguments\nas a key for a different mini state machine.\n\n```js\n const { action: { load }, getters: { getData, getMeta } } =\n   ragFactoryMap({ load: Function })\n\n // Somewhere else, maybe in a component after `load` was passed in through mapDispatchToProps.\n\n ...\n componentDidMount() {\n   const { loaded, load, taskId, userId } = this.props;\n   !loaded && load(userId, taskId);\n }\n ...\n mapStateToProps(state, props) {\n   const { userId, taskId } = props;\n   return {\n     loaded: getMeta(userId, taskId).loaded,\n     taskData: getData(userId, taskId)\n   }\n }\n```\n\n## Pre Requisites\nYou'll need `redux-thunk` and to reformate your `createRootReducer` function. We'll need to handle the addition of dynamic reducers!\n\nHere's what your redux store creation will look like:\n```js\nimport { combineReducers, createStore, applyMiddleware } from 'redux';\nimport thunk from 'redux-thunk';\n\nimport { combineAsyncReducers, configureRags } from 'redux-rags';\n\n// ... import a few reducers, we'll use userReducer as an example\nimport userReducer from './userReducer';\n\nconst createRootReducer = (dynamicReducers: Object = {}) => {\n  dynamicReducers = combineAsyncReducers(combineReducers, dynamicReducers);\n\n  return combineReducers({\n    ...dynamicReducers,\n    // Then list your reducers below\n    userReducer\n  });\n}\n\nconst middleware = applyMiddleware(thunk);\nconst store = createStore(createRootReducer(), compose(middleware));\nconfigureRags(store, createRootReducer);\n```\n\n## What Actions and Getters are there?\nGetters take in the full store as the argument. For `ragFactory`, you'll have access to the following getters:\n\n- `get`: Returns `BoilerState<T>`, so an object that looks like `{ data, meta }`.\n- `getData`: Returns the `data` attribute from `BoilerState<T>`. It would be type `?T`, whatever your load / update functions passed to the `ragFactory` return.\n- `getMeta`: Returns the `meta` attribute from `BoilerState<T>`. This will be an object that looks like `{ loading, lastLoadTime, errors }`, with a few more properties. Look for the types / additional properties in the type definition.\n- `getIsLoading`: Returns the `loading` attribute from the `meta` field. A common use case is for quick access to the loading state, so a special getter is provided for this meta attribute.\n\nActions are implementations of commonly used features to manipulate the state. Most of these are action creators, with `load` and `update` being thunks.\n\n- `beginLoading`: Sets `loading` meta property to true.\n- `endLoading`: Sets `loading` meta property to false.\n- `reset`: Returns the subreducer to the initial state.\n- `errors`: Sets the errors meta value.\n- `clearErrors`: Clears the errors meta value.\n- `updateData`: Sets the data value for the subreducer.\n- `update`: Thunk. Calls the `update` function passed in to `ragsFactory` and sets the data value to the result. Might also set the error attribute if the `update` function throws an error.\n- `load`: Thunk. Calls the appropriate sequence of `beginLoading`, `updateData`, `endLoading` actions while calling the `load` function passed to `ragsFactory`. If there are errors while executing, the error meta value will be set.\n\n## Details and explanations of exports\n\n### A few types to consider:\nThe generated subreducer uses the following type for the state:\n```js\ntype BoilerState<T> = {\n  data: ?T,\n  meta: {\n    loading: boolean,\n    loaded: boolean,\n    changeCount: number,\n    lastChangeTime: ?number,\n    errors?: *\n  }\n}\n```\n|  Meta Property | Description |\n|:----:|:---:|\n| loading | Is the data currently being loaded |\n| loaded | Has the data every been loaded? Equivalent to `changeCount > 0` |\n| changeCount | Number of times the data has been loaded / updated |\n| lastChangeTime | The time of the last change |\n| errors | Error object from the loading function, if any |\n\n### configureRags\nSignature: `(store: Object, createRootReducer: Function) => void`.\n\nThis function is how you set up the module, giving it access to your redux store. This is used to configure the reducer\ninjector. We need `store` for the [`replaceReducer`](https://redux.js.org/api/store#replaceReducer) method. And we'll need to have a formatted `createRootReducer` function that accepts dynamic reducers. We've provided a `combineAsyncReducers` function to make this easier.\n\n### combineAsyncReducers\nSignature: `(combineReducers: Function, dynamicReducers: Object) => newDynamicReducers`\n\nThis function is designed to recursively call the `combineReducers` function on the second parameter `dynamicReducers`. This means that whatever the structure of `dynamicReducers` we can register the functions with `redux`. The package internally will add values underneath `@@redux-rags` and `@@redux-rags/map` keys in the `dynamicReducers` object. \n\n### injectReducer\nSignature: `(keys: Array<string>, reducer: Function) => void`\n\nThis function will insert the given `reducer` at the nested key location in `dynamicReducers` specified by `keys`. For example, if the user has called `ragFactory` with the name `'user info'` internally we will call this function with `injectReducer(['@@redux-rags', 'user info'], subreducer)`. This will set values so that `dynamicReducers['@@redux-rags']['user info'] == subreducer` and update the store with the newly injected subreducer.\n\nYou can call this function and inject your own subreducers. You could just use `configureRags`, `combineAsyncReducers`, and `injectReducer` to setup your store to allow for dynamic reducers and skip the rest of this module. \n\n### ragFactory\n\nThough both `load` and `update` are optional, you pass at least one. With neither of these, there is no good way to\nupdate the data or metadata fields.\n\nThe `ragFactory` will place the dynamically added subreducers in the `@@redux-rags` top leve of the redux store.\nIf you want to place this somewhere else, just put the returned `subreducer` somewhere in your redux store and\npass in the `getInStore` function.\n\n|  Props  |  Type  |  Optional  |  Description  |\n|:-------:|:------:|:----------:|:-------------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to redux and the automatically injected subreducer will use that name. |\n| load | Function | yes | A function that loads data |\n| getInitialState | Function | yes | Function to create initial data for the return of `load`, will use null if not defined |\n| getInStore | Function | yes | `(state) => state.some.path.to.subreducer` Function to locate the subreducer in the store. Will place in default location if not specified, otherwise will use this location in getters. |\n| update | Function | yes | (dataValue, ...args) => { return newData; } A function to manipulate the data attribute. Returned `update` will have signature `(...args) => void` |\n| loadOnlyOnce | boolean | yes | Prevent the `load` function from being called more than once. |\n| partialReducer | Function | yes | Allows you to hook in to other actions in the redux store and modify the state. You'll be working with the BoilerState argument. Extend the reducer and listen to other actions, for example you can could clear the data on user logout. Write this like a reducer to extend the functionality of the generated boilerplate. You can also delay assignment if you want to utilize actions in the returned Actions object, just assign the function to `subreducer.partialReducer`.|\n\n### ragFactoryMap\nWhat's this `ragFactoryMap`? Well if you want to cache data based on the query parameters, then the `ragFactoryMap` is for you!\nFor each collection of args, a new reducer will be injected. The params are stringified and used as the key for the injected subreducer.\n\nThe `ragFactoryMap` builds on top of `ragFactory`. That factory is used internally to generate the subreducers and actions for each key.\n\n| Props | Type | Optional | Description |\n|:-----:|:----:|:--------:|:-----------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to redux and the automatically injected subreducer will use that name. |\n| load | Function | no | A function that loads data. The returned function will have the same signature. |\n| getInitialState | Function | yes | Function to create initial data for the return of load, will use null if not defined |\n\n\n## License\nMIT\n","gitHead":"fbb03358ba61642202ffa4657ca70f1cd5f8b863","scripts":{"flow":"flow","jest":"jest --maxWorkers=2","lint":"eslint src/**","test":"npm run jest","build":"babel src -d build --copy-files","jest:watch":"npm run jest -- --watchAll","lint:watch":"esw -w lib/**","prepublish":"npm run build"},"_npmUser":{"name":"kyleatcodesignal","email":"kyle@codesignal.com"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.","repository":{"url":"git+https://github.com/CodeSignal/redux-rags.git","type":"git"},"_npmVersion":"5.6.0","description":"Redux Reducers, Actions, and Getters. Simplified!","directories":{},"_nodeVersion":"8.9.4","_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"jest":"^23.6.0","redux":"^4.0.1","eslint":"^5.8.0","flow-bin":"^0.86.0","babel-cli":"^6.26.0","babel-core":"^6.26.3","babel-jest":"^23.6.0","babel-eslint":"^10.0.1","eslint-watch":"^4.0.2","babel-preset-env":"^1.7.0","babel-preset-flow":"^6.23.0","babel-preset-es2015":"^6.24.1","babel-preset-stage-3":"^6.24.1","eslint-plugin-import":"^2.14.0","babel-plugin-transform-runtime":"^6.23.0","babel-plugin-dynamic-import-node":"^2.2.0","babel-plugin-syntax-dynamic-import":"^6.18.0","babel-plugin-transform-class-properties":"^6.24.1"},"_npmOperationalInternal":{"tmp":"tmp/redux-rags_0.1.0_1542047144361_0.37991584605479645","host":"s3://npm-registry-packages"}},"0.1.0-rc1":{"name":"redux-rags","version":"0.1.0-rc1","keywords":["redux"],"author":{"name":"CodeSignal"},"license":"MIT","_id":"redux-rags@0.1.0-rc1","maintainers":[{"name":"kyleatcodesignal","email":"kyle@codesignal.com"}],"homepage":"https://github.com/CodeSignal/redux-rags#readme","bugs":{"url":"https://github.com/CodeSignal/redux-rags/issues"},"dist":{"shasum":"98ff0ceede68d979f5f0052d90507543ba9672fe","tarball":"https://registry.npmjs.org/redux-rags/-/redux-rags-0.1.0-rc1.tgz","fileCount":10,"integrity":"sha512-JRzVc1v+hXreNbHoVTP7vS/V5mieedEPXj2cGtGRDtwXrCzaKk6DMy/Xcl63eNIaIK6pOqs3d6uJqx/7b3HPgQ==","signatures":[{"sig":"MEUCIQCuFL0tVkBHxzkmMMnuATMYmvOvj884bBCLXzJKgoMltQIgP8hjWmlt50rLlAsj//7/4Ik3+SM7k3wZG7pUBNKSwpM=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":39291,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb6cdcCRA9TVsSAnZWagAABfoQAJf1xNRQQvnhYkUMwBqb\nRgQ30vjPTXREw4lJwqwSrB3p9J//1p1iblGhVfEK+C+oyqSRIxh+MWId+gnr\nS1+5QRUi/tkLLVFiGXEH6Sz9HV0/wJ9P9pjy0LFDieE87x5pq8qnCwdxyabg\nWFdbPwh5SH5s9oJpWlF8wCFW867vwWWoVQeacpthPeu5w+wpux5Q376Nc4H1\nkO0yasKYGvKi0V+dtKQs5yfoRLzH2QMLnGGYSVjXYl4Wax1edTSv3v09Uw0x\nay4YzxP3ssou1n9vl2wKDR5853uEeQnKJRBLfaVHyw0T8OWbM3Fl6HsG1+Nv\n/kMLbajQMIBLfMjx8xJdwlUSXPxJl7+Fg/CV6yJekqbknSX2EEBLG4hEZFdf\n9/FEwtgu9TkUWIEJ8cfj5Cb9+N+asidSUQKXt4zLrWxW8Qk4QU/iKaqxLpuo\nsjhmC13HhG8+wO40RVVl/BiMDN9UpW1qVRfyM97ZsVXCpsPuHZj8Rev4LLI5\n9NivNw8gbl8UnmVn8UxPp4tY7gKbcUAYpGxSUuyYD9tWpFQLJELoMI3s0oqR\nmP6j/obWQ661564FYiN10oP/f+pPg9iF7SOix24/Gt5uTEkdgSAy330WN4Qm\nfKshMN46TxsiaLiuG+ksbzFG+V+0WFhTChm4sCU2icF+xF5fNAwo03cAtgG1\nO/EL\r\n=O0Ou\r\n-----END PGP SIGNATURE-----\r\n"},"jest":{"testMatch":["<rootDir>/**/?(*.)+(test).js"],"transform":{"^.+\\.js$":"<rootDir>/test/jest.transform.js"},"modulePaths":["<rootDir>/"],"moduleFileExtensions":["js"]},"main":"build/index.js","readme":"# Redux-Rags\nRedux Reducers, Actions, and Generators: Simplified!\n\nGenerate and automatically inject reducers that manage the `begin load -> load -> endLoad -> set data || set error` lifecycle for data fetching.\n\nTLDR: `({ load }) => ({ actions: { load }, getters: { getData } })`\n\n## Motivation\nWe found that a lot of our reducers were redefining a common theme. We have some endpoint\nthat we want to query for data. So we'll want a `begin loading` action, so we can render\na fancy loading spinner. Then we'll hit the endpoint, maybe it returns data, maybe we get\nan error. But we definitely stopped loading. And that's it. A mini-state machine that we\nredefined dozens of times. But what if there were an easier way? What if we could define\nthat mini state machine once and re-use the logic with each query? Well `redux-rags` is\nhere to help clean up all the spilled copy-pasta from your redux boilerplate!\n\nHuge thanks to Jimmy Shen for [this amazing Medium article](https://medium.com/@jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7)\non dynamically injecting reducers into the redux store.\n\n## Usage\n\n### Simplest Example\nHere we want to hit an endpoint with the `userId` parameter. We'll avoid worrying about loading states or anything for now. The call to `ragFactory` creates the actions and injects the subreducer for us. Then this state information is stored in redux, so when users return to the component they'll see the cached data. \n\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { ragFactory } from 'redux-rags';\n\n// Pass load to factory, factory returns a load that we can give to redux, will have the same signature as the function we passed in.\nconst { actions: { load }, getters: { getData } } = ragFactory(\n  load: (userId) => axios.get('/users', { userId })\n});\n\nfunction ViewUser({userId, load, user}) {\n  return (\n    <div>\n      <button onClick={() => load(userId)}>Load User Info</button>\n      {user && <UserData data={user} />}\n    </div>\n  )\n}\n\nexport default connect(state => ({ user: getData(state)}), { load })(ViewUser)\n```\n\n### Using the `ragFactory` in another file\nHere's how you'd interact with redux for a data request. The returned `load` function will take the same arguments as the `load` function passed in.\n```js\nimport { ragFactory } from 'redux-rags';\n\nconst { actions: { load }, getters: { getData, getIsLoading } } =\n  ragFactory({ load: () => axios.get('/faq') });\n\nexport {\n  loadFaq: load,\n  getFaq: getData,\n  getIsFaqLoading: getIsLoading\n};\n```\n\n#### Use that Data Request in a React Component\nImport the Getters and Action as you normally would and use them with `react-redux`.\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { loadFaq, getFaq, getIsFaqLoading } from './faqData';\nimport Loading from './Loading';\nimport FaqItem from './FaqItem';\n\nclass Faq extends React.Component {\n  componentDidMount() {\n    this.props.loadFaq();\n  }\n\n  render() {\n    const { isLoading, faqData } = this.props;\n    if (isLoading) {\n      return <Loading />;\n    }\n\n    return (\n      <React.Fragment>\n        {faqData.map((data, index) => <FaqItem key={index} data={data} />}\n      </React.Fragment>\n    )\n  }\n}\n\nconst mapStateFromProps = state => ({\n  faqData: getFaq(state),\n  isLoading: getIsFaqLoading(state)\n});\n\nexport default connect(mapStateFromProps, { loadFaq })(Faq);\n```\n\n### Placing the subreducer in a custom location in redux\nConnect subreducer to your desired location in the redux store, and tell the generator\nwhere you put it. Getters are also factories for you. If you don't care where the\nsubreducer lives, don't pass in a `getInStore` method and the generator will place it for you.\nIn this case taking `subreducer` and adding it to the `combineSubreducers` function in the `currentUser` subreducer. (But you can place\nit wherever you want, just update the `getInStore` function passed to `ragFactory`.\n```js\nimport { generator };\nconst { actions, subreducer, getters } = generator({\n  name: 'MY_DATA',\n  load: (param) => methodService.run('fetchSomething', param),\n  getInStore: (store) => store && store.currentUser && store.currentUser.my_data\n});\n```\n\n### Using update to manipulate the stored value\nIn addition to `load` you can also pass in an `update` function, which will take the current `data` value as the first parameter. An example of this might be an update function that increments the current value by 2. The signature of the returned function is slightly different, as the `data` first argument is passed internally, so you pass in a function that takes parameters `(currentData, ...additionalParams)` and the returned `actions.update` function will take the parameters `(...additionalParams)`.\n```js\nimport { generator };\nconst { actions : { update }, getters } = generator({\n  name: 'MY_DATA',\n  update: (currentData) => currentData + 2,\n});\n```\n\n### Using the ragFactoryMap\nNot much to say, it's pretty much how you would use `ragFactory`, except treating the input arguments\nas a key for a different mini state machine.\n\n```js\n const { action: { load }, getters: { getData, getMeta } } =\n   ragFactoryMap({ load: Function })\n\n // Somewhere else, maybe in a component after `load` was passed in through mapDispatchToProps.\n\n ...\n componentDidMount() {\n   const { loaded, load, taskId, userId } = this.props;\n   !loaded && load(userId, taskId);\n }\n ...\n mapStateToProps(state, props) {\n   const { userId, taskId } = props;\n   return {\n     loaded: getMeta(userId, taskId).loaded,\n     taskData: getData(userId, taskId)\n   }\n }\n```\n\n## Pre Requisites\nYou'll need `redux-thunk` and to reformate your `createRootReducer` function. We'll need to handle the addition of dynamic reducers!\n\nHere's what your redux store creation will look like:\n```js\nimport { combineReducers, createStore, applyMiddleware } from 'redux';\nimport thunk from 'redux-thunk';\n\nimport { combineAsyncReducers, configureRags } from 'redux-rags';\n\n// ... import a few reducers, we'll use userReducer as an example\nimport userReducer from './userReducer';\n\nconst createRootReducer = (dynamicReducers: Object = {}) => {\n  dynamicReducers = combineAsyncReducers(combineReducers, dynamicReducers);\n\n  return combineReducers({\n    ...dynamicReducers,\n    // Then list your reducers below\n    userReducer\n  });\n}\n\nconst middleware = applyMiddleware(thunk);\nconst store = createStore(createRootReducer(), compose(middleware));\nconfigureRags(store, createRootReducer);\n```\n\n## What Actions and Getters are there?\nGetters take in the full store as the argument. For `ragFactory`, you'll have access to the following getters:\n\n- `get`: Returns `BoilerState<T>`, so an object that looks like `{ data, meta }`.\n- `getData`: Returns the `data` attribute from `BoilerState<T>`. It would be type `?T`, whatever your load / update functions passed to the `ragFactory` return.\n- `getMeta`: Returns the `meta` attribute from `BoilerState<T>`. This will be an object that looks like `{ loading, lastLoadTime, errors }`, with a few more properties. Look for the types / additional properties in the type definition.\n- `getIsLoading`: Returns the `loading` attribute from the `meta` field. A common use case is for quick access to the loading state, so a special getter is provided for this meta attribute.\n\nActions are implementations of commonly used features to manipulate the state. Most of these are action creators, with `load` and `update` being thunks.\n\n- `beginLoading`: Sets `loading` meta property to true.\n- `endLoading`: Sets `loading` meta property to false.\n- `reset`: Returns the subreducer to the initial state.\n- `errors`: Sets the errors meta value.\n- `clearErrors`: Clears the errors meta value.\n- `updateData`: Sets the data value for the subreducer.\n- `update`: Thunk. Calls the `update` function passed in to `ragsFactory` and sets the data value to the result. Might also set the error attribute if the `update` function throws an error.\n- `load`: Thunk. Calls the appropriate sequence of `beginLoading`, `updateData`, `endLoading` actions while calling the `load` function passed to `ragsFactory`. If there are errors while executing, the error meta value will be set.\n\n## Details and explanations of exports\n\n### A few types to consider:\nThe generated subreducer uses the following type for the state:\n```js\ntype BoilerState<T> = {\n  data: ?T,\n  meta: {\n    loading: boolean,\n    loaded: boolean,\n    changeCount: number,\n    lastChangeTime: ?number,\n    errors?: *\n  }\n}\n```\n|  Meta Property | Description |\n|:----:|:---:|\n| loading | Is the data currently being loaded |\n| loaded | Has the data every been loaded? Equivalent to `changeCount > 0` |\n| changeCount | Number of times the data has been loaded / updated |\n| lastChangeTime | The time of the last change |\n| errors | Error object from the loading function, if any |\n\n### configureRags\nSignature: `(store: Object, createRootReducer: Function) => void`.\n\nThis function is how you set up the module, giving it access to your redux store. This is used to configure the reducer\ninjector. We need `store` for the [`replaceReducer`](https://redux.js.org/api/store#replaceReducer) method. And we'll need to have a formatted `createRootReducer` function that accepts dynamic reducers. We've provided a `combineAsyncReducers` function to make this easier.\n\n### combineAsyncReducers\nSignature: `(combineReducers: Function, dynamicReducers: Object) => newDynamicReducers`\n\nThis function is designed to recursively call the `combineReducers` function on the second parameter `dynamicReducers`. This means that whatever the structure of `dynamicReducers` we can register the functions with `redux`. The package internally will add values underneath `@@redux-rags` and `@@redux-rags/map` keys in the `dynamicReducers` object. \n\n### injectReducer\nSignature: `(keys: Array<string>, reducer: Function) => void`\n\nThis function will insert the given `reducer` at the nested key location in `dynamicReducers` specified by `keys`. For example, if the user has called `ragFactory` with the name `'user info'` internally we will call this function with `injectReducer(['@@redux-rags', 'user info'], subreducer)`. This will set values so that `dynamicReducers['@@redux-rags']['user info'] == subreducer` and update the store with the newly injected subreducer.\n\nYou can call this function and inject your own subreducers. You could just use `configureRags`, `combineAsyncReducers`, and `injectReducer` to setup your store to allow for dynamic reducers and skip the rest of this module. \n\n### ragFactory\n\nThough both `load` and `update` are optional, you pass at least one. With neither of these, there is no good way to\nupdate the data or metadata fields.\n\nThe `ragFactory` will place the dynamically added subreducers in the `@@redux-rags` top leve of the redux store.\nIf you want to place this somewhere else, just put the returned `subreducer` somewhere in your redux store and\npass in the `getInStore` function.\n\n|  Props  |  Type  |  Optional  |  Description  |\n|:-------:|:------:|:----------:|:-------------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to redux and the automatically injected subreducer will use that name. |\n| load | Function | yes | A function that loads data |\n| getInitialState | Function | yes | Function to create initial data for the return of `load`, will use null if not defined |\n| getInStore | Function | yes | `(state) => state.some.path.to.subreducer` Function to locate the subreducer in the store. Will place in default location if not specified, otherwise will use this location in getters. |\n| update | Function | yes | (dataValue, ...args) => { return newData; } A function to manipulate the data attribute. Returned `update` will have signature `(...args) => void` |\n| loadOnlyOnce | boolean | yes | Prevent the `load` function from being called more than once. |\n| partialReducer | Function | yes | Allows you to hook in to other actions in the redux store and modify the state. You'll be working with the BoilerState argument. Extend the reducer and listen to other actions, for example you can could clear the data on user logout. Write this like a reducer to extend the functionality of the generated boilerplate. You can also delay assignment if you want to utilize actions in the returned Actions object, just assign the function to `subreducer.partialReducer`.|\n\n### ragFactoryMap\nWhat's this `ragFactoryMap`? Well if you want to cache data based on the query parameters, then the `ragFactoryMap` is for you!\nFor each collection of args, a new reducer will be injected. The params are stringified and used as the key for the injected subreducer.\n\nThe `ragFactoryMap` builds on top of `ragFactory`. That factory is used internally to generate the subreducers and actions for each key.\n\n| Props | Type | Optional | Description |\n|:-----:|:----:|:--------:|:-----------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to redux and the automatically injected subreducer will use that name. |\n| load | Function | no | A function that loads data. The returned function will have the same signature. |\n| getInitialState | Function | yes | Function to create initial data for the return of load, will use null if not defined |\n\n\n## License\nMIT\n","gitHead":"5244c46b2e2067a24758f65ae21e9fbf49d9f657","scripts":{"flow":"flow","jest":"jest --maxWorkers=2","lint":"eslint src/**","test":"npm run jest","build":"babel src -d build --copy-files","jest:watch":"npm run jest -- --watchAll","lint:watch":"esw -w lib/**","prepublish":"npm run build"},"_npmUser":{"name":"kyleatcodesignal","email":"kyle@codesignal.com"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.","repository":{"url":"git+https://github.com/CodeSignal/redux-rags.git","type":"git"},"_npmVersion":"5.6.0","description":"Redux Reducers, Actions, and Getters. Simplified!","directories":{},"_nodeVersion":"8.9.4","_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"jest":"^23.6.0","redux":"^4.0.1","eslint":"^5.8.0","flow-bin":"^0.86.0","babel-cli":"^6.26.0","babel-core":"^6.26.3","babel-jest":"^23.6.0","babel-eslint":"^10.0.1","eslint-watch":"^4.0.2","babel-preset-env":"^1.7.0","babel-preset-flow":"^6.23.0","babel-preset-es2015":"^6.24.1","babel-preset-stage-3":"^6.24.1","eslint-plugin-import":"^2.14.0","babel-plugin-transform-runtime":"^6.23.0","babel-plugin-dynamic-import-node":"^2.2.0","babel-plugin-syntax-dynamic-import":"^6.18.0","babel-plugin-transform-class-properties":"^6.24.1"},"_npmOperationalInternal":{"tmp":"tmp/redux-rags_0.1.0-rc1_1542047579482_0.388163847330101","host":"s3://npm-registry-packages"}},"1.0.0":{"name":"redux-rags","version":"1.0.0","keywords":["redux"],"author":{"name":"CodeSignal"},"license":"MIT","_id":"redux-rags@1.0.0","maintainers":[{"name":"kyleatcodesignal","email":"kyle@codesignal.com"}],"homepage":"https://github.com/CodeSignal/redux-rags#readme","bugs":{"url":"https://github.com/CodeSignal/redux-rags/issues"},"dist":{"shasum":"727d330f5f3285e3f4b2576ae901bb2d1cffbac6","tarball":"https://registry.npmjs.org/redux-rags/-/redux-rags-1.0.0.tgz","fileCount":10,"integrity":"sha512-WpUIX14Uch2QEjwCXDXQZaNe5zrRXEyDjP9DPv26I/OQ9KUgCqXQcvlcQDjnF9PkZYpmk+uzQwXRKNzW1tzo3w==","signatures":[{"sig":"MEQCIHv2HaIx82+N519CvOOm207oDwu+QIXXif6FDbEG2YGJAiAnm3Oq2efooooGoogd40NFcWpDNzkQuhHpYyLT4mPV1g==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":39287,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb6cgzCRA9TVsSAnZWagAAX6YP/3Yv30IqngRM7DTStUMe\n5Eg+d6YOh1Mkqbs+HNtZRJOOMWgClpMzxoP8EK+cpphEG7dpOt2FRiO3vsbN\nOv/xmxkkJepvmeNrdyWpHPfwMtqg0bBmoVkO0ETt+6Gb9aAhOEm2mhks46bM\n2TXxfzd7slzhtU5FvwBuYo2v2kJubEmP+qbQ0xLmWal0CTfehWKk3jHW0YRG\ni6D+zeZyKFLOU2bzDmmFIVd7EmpnYjephiLEmldgpTTDXYeox3YllGxkNGD3\nQz6gGL4bTF54cVdMKT4KQYo6NUuhc616AkLZ9PIVq/6IFnDK1H6qLs7yVNe6\nl9P3lXWgAVfejZaWfiBlpse+Fiq/qj/AeHT3oznVMOglWs60QxIIa38l+ZPk\n6gEvBqwD88LnvZH6hQaxpL08SJx5gf4/yjSFyCp168c7G0og9r5fK5cX0nFP\nYFOqYMJO4kaX1U9g1smJf/JU7gxAyCYL7O6hD3ld6oAq4ND9dZmOk7GwrxRM\nH7McnU2qMHyLbIXmCAlXhnjmLK4/qJTpdJ1By0Zz9MoSQHMEWmv2Lx8D2lhe\nPXS0lusm/cw9s3EdgnBiNTL5rmIg6fBX+Ut2TFmNd0HrQXFbAnn3LOoqonJS\nkt0txn+705dJZ8NpDEAJUrKX4Dv+QjTAkpQd2GEdeCmQLD/YgR6LsNfch94w\nRgOy\r\n=aXfy\r\n-----END PGP SIGNATURE-----\r\n"},"jest":{"testMatch":["<rootDir>/**/?(*.)+(test).js"],"transform":{"^.+\\.js$":"<rootDir>/test/jest.transform.js"},"modulePaths":["<rootDir>/"],"moduleFileExtensions":["js"]},"main":"build/index.js","gitHead":"6a28a14d53c21d694ab6505b4398d53d1f49f0ae","scripts":{"flow":"flow","jest":"jest --maxWorkers=2","lint":"eslint src/**","test":"npm run jest","build":"babel src -d build --copy-files","jest:watch":"npm run jest -- --watchAll","lint:watch":"esw -w lib/**","prepublish":"npm run build"},"_npmUser":{"name":"kyleatcodesignal","email":"kyle@codesignal.com"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.","repository":{"url":"git+https://github.com/CodeSignal/redux-rags.git","type":"git"},"_npmVersion":"5.6.0","description":"Redux Reducers, Actions, and Getters. Simplified!","directories":{},"_nodeVersion":"8.9.4","_hasShrinkwrap":false,"devDependencies":{"jest":"^23.6.0","redux":"^4.0.1","eslint":"^5.8.0","flow-bin":"^0.86.0","babel-cli":"^6.26.0","babel-core":"^6.26.3","babel-jest":"^23.6.0","babel-eslint":"^10.0.1","eslint-watch":"^4.0.2","babel-preset-env":"^1.7.0","babel-preset-flow":"^6.23.0","babel-preset-es2015":"^6.24.1","babel-preset-stage-3":"^6.24.1","eslint-plugin-import":"^2.14.0","babel-plugin-transform-runtime":"^6.23.0","babel-plugin-dynamic-import-node":"^2.2.0","babel-plugin-syntax-dynamic-import":"^6.18.0","babel-plugin-transform-class-properties":"^6.24.1"},"_npmOperationalInternal":{"tmp":"tmp/redux-rags_1.0.0_1542047795317_0.6412021122699532","host":"s3://npm-registry-packages"}},"1.0.1":{"name":"redux-rags","version":"1.0.1","keywords":["redux"],"author":{"name":"CodeSignal"},"license":"MIT","_id":"redux-rags@1.0.1","maintainers":[{"name":"kyleatcodesignal","email":"kyle@codesignal.com"}],"homepage":"https://github.com/CodeSignal/redux-rags#readme","bugs":{"url":"https://github.com/CodeSignal/redux-rags/issues"},"dist":{"shasum":"d6a6d3aa7e08a53e371e077f87d2105a79dcf069","tarball":"https://registry.npmjs.org/redux-rags/-/redux-rags-1.0.1.tgz","fileCount":10,"integrity":"sha512-RBwVwrbxtOXalMwr0NWYythPVkMTI4a4DX7HVLYTrWl3Jl4QxK/4JU/ZTP8dGhi+lifQGGVD3eoKpMhStUqGYg==","signatures":[{"sig":"MEUCIFTStLhF3Hb8C2ZgS723e9uceV5QVr0/y1vVeKOIgmUZAiEA9xZCZzg+FWRcdNu2PhKSNcaAdO7mWiJAt+eZbuUF+tg=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":40983,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb6jY2CRA9TVsSAnZWagAA5N8P/js1kMnho4ku2/leWPgK\nUC8hwG0f1qZ4yGY9pbUjAmQ5F5/CJMxCmx1NiGMPKITlCDY0FGKIjeN11Lf1\nUWkBcc7YoOMTdSTbesFgSTYv/8xyJOQ5n9nNAs51qVxOgyiZ+e4ps9WdWL15\n3gPePlvSgLtHkiT6cwOok54FPzL4qr37HGsrk46rTEauIYa7hJ8R/VrEYKCU\nBUov9ci2EbkoabrkBV+fYJGzcgKiHLBNIuI4jwSLVhs2lTF9zM0ejmfgm2uC\n+L3xdNqOPpaQHMn8NxsJMxQOga9AEcDyIBX4GvatQ51h2q7XDYC5ExPsOiS2\n0/5FjiErHzmRgW3UFhkXHcuqwIwgwW4922fo39PkDbI0dSXvBk3Tupzk/U1Z\nyzC0lrPOltGCNy7cGJVuz1Tw3iD5y+X0NQN4c3aSA860ol9Cn++bZ85Dy/A1\nf2gOW2gYS1LAPJniVOgJEfccz1rQ1mAfR219DqZGrdZPNdnvXqPwHLKjq+JB\nEFkzhukIWX/R54BqaE3AJe13NXCMOZqkTC/5IhGv0FyjN4FxmHOhpXa/zOXY\n6F7VrmaPbnEiQ2cGdMPU1FMl6+a5NTT4h24BXbtofrnGCpaQnB46dAGCJ3Sm\nC3tojznvLmeKwXJlqQMIRwSps2AxHWeLsvR6z7YBth7M90Y+r1GE+wZIQLV7\nZgPs\r\n=Ajzh\r\n-----END PGP SIGNATURE-----\r\n"},"jest":{"testMatch":["<rootDir>/**/?(*.)+(test).js"],"transform":{"^.+\\.js$":"<rootDir>/test/jest.transform.js"},"modulePaths":["<rootDir>/"],"moduleFileExtensions":["js"]},"main":"build/index.js","gitHead":"356ecdc0c486353694e8bdb67fb4e35e9b8de3d9","scripts":{"flow":"flow","jest":"jest --maxWorkers=2","lint":"eslint src/**","test":"npm run jest","build":"babel src -d build --copy-files","jest:watch":"npm run jest -- --watchAll","lint:watch":"esw -w lib/**","prepublish":"npm run build"},"_npmUser":{"name":"kyleatcodesignal","email":"kyle@codesignal.com"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.","repository":{"url":"git+https://github.com/CodeSignal/redux-rags.git","type":"git"},"_npmVersion":"5.6.0","description":"Redux Reducers, Actions, and Getters. Simplified!","directories":{},"_nodeVersion":"8.9.4","dependencies":{"babel-runtime":"^6.26.0"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^23.6.0","redux":"^4.0.1","eslint":"^5.8.0","flow-bin":"^0.86.0","babel-cli":"^6.26.0","babel-core":"^6.26.3","babel-jest":"^23.6.0","babel-eslint":"^10.0.1","eslint-watch":"^4.0.2","babel-preset-env":"^1.7.0","babel-preset-flow":"^6.23.0","babel-preset-es2015":"^6.24.1","babel-preset-stage-3":"^6.24.1","eslint-plugin-import":"^2.14.0","babel-plugin-transform-runtime":"^6.23.0","babel-plugin-dynamic-import-node":"^2.2.0","babel-plugin-syntax-dynamic-import":"^6.18.0","babel-plugin-transform-class-properties":"^6.24.1"},"_npmOperationalInternal":{"tmp":"tmp/redux-rags_1.0.1_1542075957368_0.07909557897748831","host":"s3://npm-registry-packages"}},"1.1.0":{"name":"redux-rags","version":"1.1.0","keywords":["redux"],"author":{"name":"CodeSignal"},"license":"MIT","_id":"redux-rags@1.1.0","maintainers":[{"name":"kyleatcodesignal","email":"kyle@codesignal.com"}],"homepage":"https://github.com/CodeSignal/redux-rags#readme","bugs":{"url":"https://github.com/CodeSignal/redux-rags/issues"},"dist":{"shasum":"58cacb855e72a0b348e5433b88d456c24dbd71f5","tarball":"https://registry.npmjs.org/redux-rags/-/redux-rags-1.1.0.tgz","fileCount":10,"integrity":"sha512-Et140cHzJWnESj1SjFh3XKXA/EIZlA5LhV5t1suJU8RVM5Gi5feRmBMX2ZMJoCHLE6aMDbfau9V+BtHwSDkVcw==","signatures":[{"sig":"MEYCIQDGxTS3oYS7cFDlGHXuPuPjn5QEtCrxs1zi0zYgQ9hKQgIhAJIvwtOe6ftg93IaoaCVq4CrG8s4uRz0E/gT+23o3V42","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":43157,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcBFTICRA9TVsSAnZWagAAt2YP/1xLuZS0D3JQx8+Mu6fY\n5PbiCQGuBVNRlTu/TQgf9uBKLxK891Xqn8enTU2+13bPWNVw9wg1Zd9jqwZF\nvgb/VmZv4t0IH5oSOMh9ahiStGwUlXPdShBvtaX4z4EHAfULApw6jH5FSc6n\n6VAjMEUG6beNPsF+xfGhaXJijPu0sbaPvpCQoa11PfXP4WK2XR2vTpydrg5p\nGKzoPn7bp/VjsPsoWYrZRH1tTv+T2lmub3YflcnN4JnhlfEldwnYOCIs/71+\nH+DRNLILSmcR2Dur/ZNJOZbHcJgbFcL9a+pLL99/Y4vzkiRSBitwqHWbiYQ+\njTRSwpjD5fTWAwIvsbZafa2ee03krAATgBZgLU+S2OZpuZ3N36Avp7T4Ac0W\n3LSTGl9HQYmIaDZKt+fBbHP91aNyi3s7qEaIMQFA7pmNo6Ztf923P20Q+8nY\n9QJuGKUlqF45SKAUmxm2hNQTeCyj3Ol8Z2dGgQ4lY9A0zM8f+Hj087SBURly\nBawTeV6gxaKQdaydgb1U1X6WMEstT0MVadxtqdIgDHIYjUW2p7yHEzFYn2sM\nGd96YRj0OMs2etlvu5aqtLJMN9zL7aREOl9Oozrwp8K7HDT14cbWFBaYkVW5\nJthDonxUf4otOEDmkRkv0rQ/iPgfOlCFAt3tj3trYVEvZpq71wugBkxgFhwQ\nytXe\r\n=/2uL\r\n-----END PGP SIGNATURE-----\r\n"},"jest":{"testMatch":["<rootDir>/**/?(*.)+(test).js"],"transform":{"^.+\\.js$":"<rootDir>/test/jest.transform.js"},"modulePaths":["<rootDir>/"],"moduleFileExtensions":["js"]},"main":"build/index.js","gitHead":"a07d75f9186d6ebf9aac97ab14284934c022b484","scripts":{"flow":"flow","jest":"jest --maxWorkers=2","lint":"eslint src/**","test":"npm run jest -- --coverage --coverageReporters=text-lcov | ./node_modules/coveralls/bin/coveralls.js","build":"babel src -d build --copy-files","jest:watch":"npm run jest -- --watchAll","lint:watch":"esw -w lib/**","prepublish":"npm run build"},"_npmUser":{"name":"kyleatcodesignal","email":"kyle@codesignal.com"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.","repository":{"url":"git+https://github.com/CodeSignal/redux-rags.git","type":"git"},"_npmVersion":"5.6.0","description":"Redux Reducers, Actions, and Getters. Simplified!","directories":{},"_nodeVersion":"8.9.4","_hasShrinkwrap":false,"devDependencies":{"jest":"^23.6.0","redux":"^4.0.1","eslint":"^5.8.0","flow-bin":"^0.86.0","babel-cli":"^6.26.0","coveralls":"^3.0.2","babel-core":"^6.26.3","babel-jest":"^23.6.0","babel-eslint":"^10.0.1","eslint-watch":"^4.0.2","babel-preset-env":"^1.7.0","babel-preset-flow":"^6.23.0","babel-preset-es2015":"^6.24.1","babel-preset-stage-3":"^6.24.1","eslint-plugin-import":"^2.14.0","babel-plugin-transform-runtime":"^6.23.0","babel-plugin-dynamic-import-node":"^2.2.0","babel-plugin-syntax-dynamic-import":"^6.18.0"},"_npmOperationalInternal":{"tmp":"tmp/redux-rags_1.1.0_1543787719485_0.749850779570727","host":"s3://npm-registry-packages"}},"1.1.1":{"name":"redux-rags","version":"1.1.1","keywords":["redux"],"author":{"name":"CodeSignal"},"license":"MIT","_id":"redux-rags@1.1.1","maintainers":[{"name":"kyleatcodesignal","email":"kyle@codesignal.com"}],"homepage":"https://github.com/CodeSignal/redux-rags#readme","bugs":{"url":"https://github.com/CodeSignal/redux-rags/issues"},"dist":{"shasum":"a82ef083e1d8b7c39f54eb74c72060b1fd7fda85","tarball":"https://registry.npmjs.org/redux-rags/-/redux-rags-1.1.1.tgz","fileCount":10,"integrity":"sha512-vcBCOF40HqWc+ymfq0k2pmouZwPtTZx0c1moxR/HBeLJjLzJuk32K4Pt9mF6pi2bHJxFvExr8m0N9AW2VOl8fA==","signatures":[{"sig":"MEUCIQC5LvcI4axh/4lzpRguHDqlADiXelXXQRTdDynbLtWokgIgIDSxzm5VN45omDbcecU8p9kj4YZ5Kg1yjVuzwHdP3P8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":31016,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcQozmCRA9TVsSAnZWagAA2X0P/RTk4AWPkQpPHOPk78c/\nnPRaeHG5DTni7PisamLPaLdfKKArhtL0brcZXY6CrlqqP79yhxH9Bh0O1p9e\nPxVC2T/woOI8bNR3+D4+iAQyorCOMMKkwlUMgFvpRFMjeiyRpbC+Hfkj7Ksq\nahkJp3u6xD6xvPdXDuyhYLmRcCSW8gphJbxnDb/uKyKWxlNAu1mfVwboYHPX\n323dRmqH5HwrF+blzluOTrIVEWcu44yS4HKymgttGHpAMmHprQ9ssNQUoDng\n5AQcsiDy2REvnjkzdxmff4lqb2wd229WnAcW/wasd8z3LlSsBrt40oWEUDnK\nCfusROp/1If5CdfYQkFADVksQz6AmQ7RPFJGrcQFQhFzV21vZgad0jyeRI8t\n6/0qyWlXY0UXTSHirUY9cIop7Xcf9OgPtlqwFzOAAnyNMDSNG3eL7fRSyrYd\nGPTzGIqUlJOphxu8x1m3MTvzME3ZmN38kzcQJ2OkLSP+wV7abKLbUbaALW85\n0w17QNLWRSx5FR3Eho/HNtXoeeWE+5UHCyIS0nT+ZbGIAJLTgFepSZUrU+a5\nF+wfAPAHQpOLKG1Xi65dH1ae0HBU+mXACX3TUHiy6glzJ41oHG+npppZfEAi\nClEtbxadcnkMIZSBUuxOsAj7Hvtp1kKBzY4l/W6zMf4zNCAn4AyceXOXlNBd\n+6cs\r\n=cnhV\r\n-----END PGP SIGNATURE-----\r\n"},"jest":{"testMatch":["<rootDir>/**/?(*.)+(test).js"],"transform":{"^.+\\.js$":"<rootDir>/test/jest.transform.js"},"modulePaths":["<rootDir>/"],"moduleFileExtensions":["js"]},"main":"build/index.js","readme":"# Redux-RAGs\n[![npm version](https://img.shields.io/npm/v/redux-rags.svg?style=flat-square)](https://www.npmjs.com/package/redux-rags)\n[![npm downloads](https://img.shields.io/npm/dm/redux-rags.svg?style=flat-square)](https://www.npmjs.com/package/redux-rags)\n[![Coverage Status](https://coveralls.io/repos/github/CodeSignal/redux-rags/badge.svg?branch=master)](https://coveralls.io/github/CodeSignal/redux-rags?branch=master)\n\nRedux **R**educers, **A**ctions, and **G**etters: Simplified!\n\nTLDR: No need to create or connect subreducers for simple async requests. Your Redux connection is handled by a single function\n```js\nconst rag = ragFactory({ load });\n\nconst newLoad = rag.actions.load;\nconst getData = rag.getters.getData;\nconnect(state => ({ data: getData(store) }), { load: newLoad })(Component);\n```\n\n## Motivation\nWe found that a lot of our reducers were redefining a common theme. We have some endpoint\nthat we want to query for data. We'd run through the same steps every time:\n* Begin loading state\n* Fetch Data\n* End Loading State\n* Set data or error\n\n<center>\n  <img src=\"media/data-flow.png\" width=\"80%\" />\n</center>\n\nAnd that's it: A mini state machine that we redefined over and over, creating dozens of\nsimilar actions and subreducers. But what if there were an easier way? What if we could\ndefine that mini state machine once and re-use the logic with each kind of query? Well `redux-rags` is\nhere to help!\n\n## Injecting a Subreducer?!\nUsing Redux requires quite a lot of boilerplate. You need to create Actions and maybe Action Creators. You need to create Reducers and place them somewhere in the store, maybe as a part of another reducer. There are a myriad of tools out there to help you simplify this process, and we're adding another to the list! This one is built around the idea that there are some pieces of state that you would like to have stored outside of a particular rendering context, but don't want to go through all the migration work to create a custom reducer in the store for this data. You want to take advantage of the benefits of Redux without trudging through all the boilerplate.\n\nSo what if you had a way to add subreducers to Redux? Then you could write code that generates the subreducer structures you use frequently and inject them! You could now start writing subreducers aimed at general cases of data interaction and vastly reduce the time it takes to migrate a piece of state to Redux. We've provided our most useful abstraction here, a simple data request lifecycle. Hopefully you can see the opportunities that injecting a subreducer provides and can use the code here as an example for how to start reusing your own subreducer logic.\n\nFor more details, I encourage you to check out [this Medium article by Jimmy Shen on subreducer injection.](https://medium.com/@jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7)\n\n## Usage\n\n\n### Single File Redux Connection Example\nHere's how you'd interact with Redux for a data request. We're going to create the actions and\nautomatically create and inject the subreducer. The returned `load` function will take the same\narguments as the `load` function passed in, so you have control over the `load` thunk.\n\nHere we want to hit an endpoint with the `userId` parameter. We'll avoid worrying about\nloading states or anything for now. The call to `ragFactory` creates the actions and injects\nthe subreducer for us. Then this state information is stored in Redux, so when users return to the\ncomponent they'll see the cached data. [Play with a similar example on CodeSandbox](https://codesandbox.io/s/k218x138l7)\n\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { ragFactory } from 'redux-rags';\n\n// Pass load to factory, factory returns basically the same load that we can give to connect.\nconst { actions: { load }, getters: { getData } } = ragFactory({\n  load: (userId) => axios.get('/users', { userId })\n});\n\nfunction ViewUser({userId, load, user}) {\n  return (\n    <div>\n      <button onClick={() => load(userId)}>Load User Info</button>\n      {user && <UserData data={user} />}\n    </div>\n  )\n}\n\nexport default connect(state => ({ user: getData(state)}), { load })(ViewUser)\n```\n\n\nAnd that's it! That's all you need to do; no need to manually create or place a subreducer\nanywhere. The subreducer is pretty basic, it holds the return of `load` in the data attribute.\nIt also keeps track of meta information, like last load time, number of times loaded, and errors.\n\nIf you want to keep track of data by parameters passed to `load`, i.e. loading a whole bunch\nof users, you'll want to use `ragFactoryMap`. This will use `ragFactory` behind the scenes to\ncreate the simple mini-reducer for each set of parameters you specify.\n\n\n## Prerequisites\nYou'll need `redux-thunk` and to restructure your `createRootReducer` function. We'll\nneed to handle the addition of dynamic reducers!\n\nHere's what your Redux store creation will look like:\n```js\nimport { combineReducers, createStore, applyMiddleware } from 'redux';\nimport thunk from 'redux-thunk';\n\nimport { combineAsyncReducers, configureRags } from 'redux-rags';\n\n// Import a few reducers like you normally would to create a top level reducer: we'll use `userReducer` as an example\nimport userReducer from './userReducer';\n\nconst createRootReducer = (dynamicReducers: Object = {}) => {\n  dynamicReducers = combineAsyncReducers(combineReducers, dynamicReducers);\n\n  return combineReducers(Object.assign({}, dynamicReducers, {\n    // Then list your reducers below\n    userReducer\n  }));\n}\n\nconst middleware = applyMiddleware(thunk);\nconst store = createStore(createRootReducer(), compose(middleware));\nconfigureRags(store, createRootReducer);\n```\n\n## What Actions and Getters are there?\nGetters take in the full store as the argument. For `ragFactory`, you'll have access to the following getters:\n\n- `get`: Returns `BoilerState<T>`, so an object that looks like `{ data, meta }`.\n- `getData`: Returns the `data` attribute from `BoilerState<T>`. It would be type `?T`, whatever your load / update functions passed to the `ragFactory` return.\n- `getMeta`: Returns the `meta` attribute from `BoilerState<T>`. This will be an object that looks like `{ loading, lastLoadTime, errors }`, with a few more properties. Look for the types / additional properties in the type definition.\n- `getIsLoading`: Returns the `loading` attribute from the `meta` field. A common use case is for quick access to the loading state, so a special getter is provided for this meta attribute.\n\nActions are implementations of commonly used features to manipulate the state. Most of these are action creators, with `load` and `update` being thunks.\n\n- `beginLoading`: Sets `loading` meta property to true.\n- `endLoading`: Sets `loading` meta property to false.\n- `reset`: Returns the subreducer to the initial state.\n- `errors`: Sets the errors meta value.\n- `clearErrors`: Clears the errors meta value.\n- `updateData`: Sets the data value for the subreducer.\n- `update`: Thunk. Calls the `update` function passed in to `ragsFactory` and sets the data value to the result. Might also set the error attribute if the `update` function throws an error. Passed in function should look like: `(dataValue, ...params) => newData`. Returned func has signature: `(...params) => newData`. This is because the current data value will be added to the function internally.\n- `load`: Thunk. Calls the appropriate sequence of `beginLoading`, `updateData`, `endLoading` actions while calling the `load` function passed to `ragsFactory`. If there are errors while executing, the error meta value will be set. Passed in function looks like: `(...params) => newData`, returned function looks like `(...params) => newData`.\n\n## Details and explanations of exports\n\n### A few types to consider:\nThe generated subreducer uses the following type for the state:\n```js\ntype BoilerState<T> = {\n  data: ?T,\n  meta: {\n    loading: boolean,\n    loaded: boolean,\n    changeCount: number,\n    lastChangeTime: ?number,\n    errors?: *\n  }\n}\n```\n|  Meta Property | Description |\n|:----:|:---:|\n| loading | Is the data currently being loaded |\n| loaded | Has the data every been loaded? Equivalent to `changeCount > 0` |\n| changeCount | Number of times the data has been loaded / updated |\n| lastChangeTime | The time of the last change |\n| errors | Error object from the `load` or `update` function, if any |\n\n### configureRags\nSignature: `(store: Object, createRootReducer: Function) => void`.\n\nThis function is how you set up the module, giving it access to your Redux store. This is used to configure the reducer\ninjector. We need `store` for the [`replaceReducer`](https://redux.js.org/api/store#replaceReducer) method. And we'll need to have a formatted `createRootReducer` function that accepts dynamic reducers. We've provided a `combineAsyncReducers` function to make this easier.\n\n### combineAsyncReducers\nSignature: `(combineReducers: Function, dynamicReducers: Object) => newDynamicReducers`\n\nThis function is designed to recursively call the `combineReducers` function on the second parameter `dynamicReducers`. This means that whatever the structure of `dynamicReducers` we can register the functions with `redux`. The package internally will add values underneath `@@redux-rags` and `@@redux-rags/map` keys in the `dynamicReducers` object. \n\n### injectReducer\nSignature: `(keys: Array<string>, reducer: Function) => void`\n\nThis function will insert the given `reducer` at the nested key location in `dynamicReducers` specified by `keys`. For example, if the user has called `ragFactory` with the name `'user info'` internally we will call this function with `injectReducer(['@@redux-rags', 'user info'], subreducer)`. This will set values so that `dynamicReducers['@@redux-rags']['user info'] == subreducer` and update the store with the newly injected subreducer.\n\nYou can call this function and inject your own subreducers. You could just use `configureRags`, `combineAsyncReducers`, and `injectReducer` to setup your store to allow for dynamic reducers and skip the rest of this module. \n\n### ragFactory\n\nThough both `load` and `update` are optional, you pass at least one. With neither of these, there is no good way to\nupdate the data or metadata fields.\n\nThe `ragFactory` will place the dynamically added subreducers in the `@@redux-rags` top leve of the Redux store.\nIf you want to place this somewhere else, just put the returned `subreducer` somewhere in your Redux store and\npass in the `getInStore` function.\n\n|  Props  |  Type  |  Optional  |  Description  |\n|:-------:|:------:|:----------:|:-------------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to Redux and the automatically injected subreducer will use that name. |\n| load | Function | yes | A function that loads data |\n| getInitialState | Function | yes | Function to create initial data for the return of `load`, will use null if not defined |\n| getInStore | Function | yes | `(state) => state.some.path.to.subreducer` Function to locate the subreducer in the store. Will place in default location if not specified, otherwise will use this location in getters. |\n| update | Function | yes | (dataValue, ...args) => { return newData; } A function to manipulate the data attribute. Returned `update` will have signature `(...args) => void` |\n| loadOnlyOnce | boolean | yes | Prevent the `load` function from being called more than once. |\n| partialReducer | Function | yes | Allows you to hook in to other actions in the Redux store and modify the state. You'll be working with the BoilerState argument. Extend the reducer and listen to other actions, for example you can could clear the data on user logout. Write this like a reducer to extend the functionality of the generated boilerplate. You can also delay assignment if you want to utilize actions in the returned Actions object, just assign the function to `subreducer.partialReducer`.|\n\n### ragFactoryMap\nWhat's this `ragFactoryMap`? Well if you want to cache data based on the query parameters, then the `ragFactoryMap` is for you!\nFor each collection of args, a new reducer will be injected. The params are stringified and used as the key for the injected subreducer.\n\nThe `ragFactoryMap` builds on top of `ragFactory`. That factory is used internally to generate the subreducers and actions for each key.\n\n| Props | Type | Optional | Description |\n|:-----:|:----:|:--------:|:-----------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to Redux and the automatically injected subreducer will use that name. |\n| load | Function | no | A function that loads data. The returned function will have the same signature. |\n| getInitialState | Function | yes | Function to create initial data for the return of load, will use null if not defined |\n\n\n## Additional Examples\n\n### Defining the Redux interactions in a separate file and importing\nWe'll use a basic fetch for this example, hitting some json endpoint. The returned load function\nhas the same signature as the function we passed in, so in this case `loadFaq` will not use\nany parameters.\n```js\nimport { ragFactory } from 'redux-rags';\n\nconst axiosLoadFaq = () => fetch('http://api.example.com/data.json');\nconst { actions, getters } = ragFactory({ load: axiosLoadFaq });\n\nexport {\n  loadFaq: actions.load,\n  getFaq: getters.getData,\n  getIsFaqLoading: getters.getIsLoading,\n};\n```\n\n#### Use that Data Request in a React Component\nImport the Getters and Action as you normally would and use them with `react-redux`. If you're used to\ndefining thunks and getters in a different file and import them, then the code below should look\nremarkably similar to code that you've already seen or even written.\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { loadFaq, getFaq, getIsFaqLoading } from './faqData';\nimport Loading from './Loading';\nimport FaqItem from './FaqItem';\n\nclass Faq extends React.Component {\n  componentDidMount() {\n    this.props.loadFaq();\n  }\n\n  render() {\n    const { isLoading, faqData } = this.props;\n    if (isLoading) {\n      return <Loading />;\n    }\n\n    return (\n      <React.Fragment>\n        {faqData.map((data, index) => <FaqItem key={index} data={data} />}\n      </React.Fragment>\n    )\n  }\n}\n\nconst mapStateFromProps = state => ({\n  faqData: getFaq(state),\n  isLoading: getIsFaqLoading(state)\n});\n\nexport default connect(mapStateFromProps, { loadFaq })(Faq);\n```\n\n### Placing the subreducer in a custom location in Redux\nConnect subreducer to your desired location in the Redux store, and tell the generator\nwhere you put it. Getters are also factories for you. If you don't care where the\nsubreducer lives, don't pass in a `getInStore` method and the generator will place it for you.\nIn this case taking `subreducer` and adding it to the `combineSubreducers` function in the `currentUser` subreducer.\nBut you can place it wherever you want, just update the `getInStore` function passed to `ragFactory`.\n```js\nimport { generator };\nconst { actions, subreducer, getters } = generator({\n  name: 'MY_DATA',\n  load: (param) => methodService.run('fetchSomething', param),\n  getInStore: (store) => store && store.currentUser && store.currentUser.my_data\n});\n```\n\n### Using update to manipulate the stored value\nIn addition to `load` you can also pass in an `update` function, which will take the\ncurrent `data` value as the first parameter. An example of this might be an update\nfunction that increments the current value by 2. The signature of the returned function\nis slightly different, as the `data` first argument is passed internally, so you pass\nin a function that takes parameters `(currentData, ...additionalParams)` and the returned\n`actions.update` function will take the parameters `(...additionalParams)`.\n[View an example that relies entirely on the `update` funciton on CodeSandbox](https://codesandbox.io/s/1307vwj8zj) \n```js\nimport { generator };\nconst { actions : { update }, getters } = generator({\n  name: 'MY_DATA',\n  update: (currentData) => currentData + 2,\n});\n```\n\n### Using the ragFactoryMap\nNot much to say, it's pretty much how you would use `ragFactory`, except treating the input arguments\nas a key for a different mini state machine.\n\n```js\n const { action: { load }, getters: { getData, getMeta } } =\n   ragFactoryMap({ load: Function });\n\n // In a component after `load` was passed in through mapDispatchToProps.\n\nclass Component extends React.Component {\n  componentDidMount() {\n    const { loaded, load, taskId, userId } = this.props;\n    // Load the data for given userId and taskId if not loaded\n    !loaded && load(userId, taskId);\n  }\n  // ... Other functions of the class\n}\n\nfunction mapStateToProps(state, props) {\n  const { userId, taskId } = props;\n  return {\n    loaded: getMeta(userId, taskId).loaded,\n    taskData: getData(userId, taskId)\n  }\n}\nexport default connect(mapStateToProps)(Component);\n```\n\n## License\nMIT\n\n## Thanks\n\nHuge thanks to Jimmy Shen for [the amazing Medium article already linked above](https://medium.com/@jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7)\non dynamically injecting reducers into the Redux store.\n","gitHead":"d2dcefe0015e012547336498de0b2edcefd488f2","scripts":{"flow":"flow","jest":"jest --maxWorkers=2","lint":"eslint src/**","test":"npm run jest -- --coverage --coverageReporters=text-lcov | ./node_modules/coveralls/bin/coveralls.js","build":"babel src -d build --copy-files","jest:watch":"npm run jest -- --watchAll","lint:watch":"esw -w lib/**","prepublish":"npm run build"},"_npmUser":{"name":"kyleatcodesignal","email":"kyle@codesignal.com"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.","repository":{"url":"git+https://github.com/CodeSignal/redux-rags.git","type":"git"},"_npmVersion":"5.6.0","description":"Redux Reducers, Actions, and Getters. Simplified!","directories":{},"_nodeVersion":"8.9.4","_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"jest":"^23.6.0","redux":"^4.0.1","eslint":"^5.8.0","flow-bin":"^0.86.0","babel-cli":"^6.26.0","coveralls":"^3.0.2","babel-core":"^6.26.3","eslint-watch":"^4.0.2","babel-preset-flow":"^6.23.0","eslint-plugin-import":"^2.14.0"},"_npmOperationalInternal":{"tmp":"tmp/redux-rags_1.1.1_1547865317663_0.43856583611962585","host":"s3://npm-registry-packages"}},"1.2.0-beta":{"name":"redux-rags","version":"1.2.0-beta","keywords":["redux"],"author":{"name":"CodeSignal"},"license":"MIT","_id":"redux-rags@1.2.0-beta","maintainers":[{"name":"kyleatcodesignal","email":"kyle@codesignal.com"}],"homepage":"https://github.com/CodeSignal/redux-rags#readme","bugs":{"url":"https://github.com/CodeSignal/redux-rags/issues"},"dist":{"shasum":"5f7db60c2d80f3d52eeb0882b3465f96631b9f02","tarball":"https://registry.npmjs.org/redux-rags/-/redux-rags-1.2.0-beta.tgz","fileCount":10,"integrity":"sha512-D8FyMK2WxhLPCxUehq9aTIW12+F5gIzLOkGIK/UMvEzTqynkwKskxh2JZOo2RwKD7YcppInshXuhN4w1bqt7kA==","signatures":[{"sig":"MEUCIDsmNlgaUm2pahvDGk4bsZDLNBKPVq0lMkvadjvLFLWJAiEAi+hh9XWycsAd0PKeIRKWsBE789ktBkyuLiYGSSdqfbg=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":42250,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcR2HFCRA9TVsSAnZWagAAzWcQAI76ivPOkzw+70o8a6DV\nXYq78ks90jxRFJfzJz7MgKxR9WzJxcdenpTnvjXgLJrz4JBOlxVFe/UaWw5g\nlbpchZhbBX89/W4UHGTkX11euYTcOO/tQMYkDGkk4QpoRuQY3+sauFIiTxTT\nWIrdIoPuTt87T8UOzuUrKBCLpxg8trmmT5ku4Zw5pgLT01aOhRwxWkw7kA+k\n4Xi/NYHivDL5d6GX1ofUj2ioEdfZrYa5Yi2ekKOnN6VuBkxzceG8kQJfoCZx\nUad9Ei8vhpm10Wr2ZQH2MOOAoyJQnI0jZgolUcom0lxCOSVcOdHkEOrEOQcJ\nUejScJRMwfQD9uylqva9yNG+neGec9IrYbKo39WCE7r/QjteUA/Q5/czIHlk\nnUG2HkzqJ2GUVbJ43kueWXufQd/wn9cyHD4EaxWwZQtS4ZFdrlqw0iQqtYai\nqBXHIGUOaIxiU8RzJq1eOTcWHQj8jSj29eyqap9KFzMmzXjLl9rUQHt3UoVA\n0DA+p/NtUgzUPivRGhYZbO/4VErrWpV5vntlPjijJMip0M+jEkiFgQX7let6\nW7klHa1HSeUKNLzKAR8n3mqwFZEbd9fny+DdwpKXEdVcfA/WgEymLHTV4aCB\nHpWJr8fUWTC2CFSAu7MjEZBumaIipsm6sHe78PyUxL7uSex1S+J+u0WTrrBa\nI84J\r\n=hQ82\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/index.js","readme":"# Redux-RAGs\n[![npm version](https://img.shields.io/npm/v/redux-rags.svg?style=flat-square)](https://www.npmjs.com/package/redux-rags)\n[![npm downloads](https://img.shields.io/npm/dm/redux-rags.svg?style=flat-square)](https://www.npmjs.com/package/redux-rags)\n[![Coverage Status](https://coveralls.io/repos/github/CodeSignal/redux-rags/badge.svg?branch=master)](https://coveralls.io/github/CodeSignal/redux-rags?branch=master)\n\nRedux **R**educers, **A**ctions, and **G**etters: Simplified!\n\nTLDR: No need to create or connect subreducers for simple async requests. Your Redux connection is handled by a single function\n```js\nconst rag = ragFactory({ load });\n\nconst newLoad = rag.actions.load;\nconst getData = rag.getters.getData;\nconnect(state => ({ data: getData(store) }), { load: newLoad })(Component);\n```\n\n## Motivation\nWe found that a lot of our reducers were redefining a common theme. We have some endpoint\nthat we want to query for data. We'd run through the same steps every time:\n* Begin loading state\n* Fetch Data\n* End Loading State\n* Set data or error\n\n<center>\n  <img src=\"media/data-flow.png\" width=\"80%\" />\n</center>\n\nAnd that's it: A mini state machine that we redefined over and over, creating dozens of\nsimilar actions and subreducers. But what if there were an easier way? What if we could\ndefine that mini state machine once and re-use the logic with each kind of query? Well `redux-rags` is\nhere to help!\n\n## Injecting a Subreducer?!\nUsing Redux requires quite a lot of boilerplate. You need to create Actions and maybe Action Creators. You need to create Reducers and place them somewhere in the store, maybe as a part of another reducer. There are a myriad of tools out there to help you simplify this process, and we're adding another to the list! This one is built around the idea that there are some pieces of state that you would like to have stored outside of a particular rendering context, but don't want to go through all the migration work to create a custom reducer in the store for this data. You want to take advantage of the benefits of Redux without trudging through all the boilerplate.\n\nSo what if you had a way to add subreducers to Redux? Then you could write code that generates the subreducer structures you use frequently and inject them! You could now start writing subreducers aimed at general cases of data interaction and vastly reduce the time it takes to migrate a piece of state to Redux. We've provided our most useful abstraction here, a simple data request lifecycle. Hopefully you can see the opportunities that injecting a subreducer provides and can use the code here as an example for how to start reusing your own subreducer logic.\n\nFor more details, I encourage you to check out [this Medium article by Jimmy Shen on subreducer injection.](https://medium.com/@jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7)\n\n## Usage\n\n\n### Single File Redux Connection Example\nHere's how you'd interact with Redux for a data request. We're going to create the actions and\nautomatically create and inject the subreducer. The returned `load` function will take the same\narguments as the `load` function passed in, so you have control over the `load` thunk.\n\nHere we want to hit an endpoint with the `userId` parameter. We'll avoid worrying about\nloading states or anything for now. The call to `ragFactory` creates the actions and injects\nthe subreducer for us. Then this state information is stored in Redux, so when users return to the\ncomponent they'll see the cached data. [Play with a similar example on CodeSandbox](https://codesandbox.io/s/k218x138l7)\n\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { ragFactory } from 'redux-rags';\n\n// Pass load to factory, factory returns basically the same load that we can give to connect.\nconst { actions: { load }, getters: { getData } } = ragFactory({\n  load: (userId) => axios.get('/users', { userId })\n});\n\nfunction ViewUser({userId, load, user}) {\n  return (\n    <div>\n      <button onClick={() => load(userId)}>Load User Info</button>\n      {user && <UserData data={user} />}\n    </div>\n  )\n}\n\nexport default connect(state => ({ user: getData(state)}), { load })(ViewUser)\n```\n\n\nAnd that's it! That's all you need to do; no need to manually create or place a subreducer\nanywhere. The subreducer is pretty basic, it holds the return of `load` in the data attribute.\nIt also keeps track of meta information, like last load time, number of times loaded, and errors.\n\nIf you want to keep track of data by parameters passed to `load`, i.e. loading a whole bunch\nof users, you'll want to use `ragFactoryMap`. This will use `ragFactory` behind the scenes to\ncreate the simple mini-reducer for each set of parameters you specify.\n\n\n## Prerequisites\nYou'll need `redux-thunk` and to restructure your `createRootReducer` function. We'll\nneed to handle the addition of dynamic reducers!\n\nHere's what your Redux store creation will look like:\n```js\nimport { combineReducers, createStore, applyMiddleware } from 'redux';\nimport thunk from 'redux-thunk';\n\nimport { combineAsyncReducers, configureRags } from 'redux-rags';\n\n// Import a few reducers like you normally would to create a top level reducer: we'll use `userReducer` as an example\nimport userReducer from './userReducer';\n\nconst createRootReducer = (dynamicReducers: Object = {}) => {\n  dynamicReducers = combineAsyncReducers(combineReducers, dynamicReducers);\n\n  return combineReducers(Object.assign({}, dynamicReducers, {\n    // Then list your reducers below\n    userReducer\n  }));\n}\n\nconst middleware = applyMiddleware(thunk);\nconst store = createStore(createRootReducer(), compose(middleware));\nconfigureRags(store, createRootReducer);\n```\n\n## What Actions and Getters are there?\nGetters take in the full store as the argument. For `ragFactory`, you'll have access to the following getters:\n\n- `get`: Returns `BoilerState<T>`, so an object that looks like `{ data, meta }`.\n- `getData`: Returns the `data` attribute from `BoilerState<T>`. It would be type `?T`, whatever your load / update functions passed to the `ragFactory` return.\n- `getMeta`: Returns the `meta` attribute from `BoilerState<T>`. This will be an object that looks like `{ loading, lastLoadTime, errors }`, with a few more properties. Look for the types / additional properties in the type definition.\n- `getIsLoading`: Returns the `loading` attribute from the `meta` field. A common use case is for quick access to the loading state, so a special getter is provided for this meta attribute.\n\nActions are implementations of commonly used features to manipulate the state. Most of these are action creators, with `load` and `update` being thunks.\n\n- `beginLoading`: Sets `loading` meta property to true.\n- `endLoading`: Sets `loading` meta property to false.\n- `reset`: Returns the subreducer to the initial state.\n- `errors`: Sets the errors meta value.\n- `clearErrors`: Clears the errors meta value.\n- `updateData`: Sets the data value for the subreducer.\n- `update`: Thunk. Calls the `update` function passed in to `ragsFactory` and sets the data value to the result. Might also set the error attribute if the `update` function throws an error. Passed in function should look like: `(dataValue, ...params) => newData`. Returned func has signature: `(...params) => newData`. This is because the current data value will be added to the function internally.\n- `load`: Thunk. Calls the appropriate sequence of `beginLoading`, `updateData`, `endLoading` actions while calling the `load` function passed to `ragsFactory`. If there are errors while executing, the error meta value will be set. Passed in function looks like: `(...params) => newData`, returned function looks like `(...params) => newData`.\n\n## Details and explanations of exports\n\n### A few types to consider:\nThe generated subreducer uses the following type for the state:\n```js\ntype BoilerState<T> = {\n  data: ?T,\n  meta: {\n    loading: boolean,\n    loaded: boolean,\n    changeCount: number,\n    lastChangeTime: ?number,\n    errors?: *\n  }\n}\n```\n|  Meta Property | Description |\n|:----:|:---:|\n| loading | Is the data currently being loaded |\n| loaded | Has the data every been loaded? Equivalent to `changeCount > 0` |\n| changeCount | Number of times the data has been loaded / updated |\n| lastChangeTime | The time of the last change |\n| errors | Error object from the `load` or `update` function, if any |\n\n### configureRags\nSignature: `(store: Object, createRootReducer: Function) => void`.\n\nThis function is how you set up the module, giving it access to your Redux store. This is used to configure the reducer\ninjector. We need `store` for the [`replaceReducer`](https://redux.js.org/api/store#replaceReducer) method. And we'll need to have a formatted `createRootReducer` function that accepts dynamic reducers. We've provided a `combineAsyncReducers` function to make this easier.\n\n### combineAsyncReducers\nSignature: `(combineReducers: Function, dynamicReducers: Object) => newDynamicReducers`\n\nThis function is designed to recursively call the `combineReducers` function on the second parameter `dynamicReducers`. This means that whatever the structure of `dynamicReducers` we can register the functions with `redux`. The package internally will add values underneath `@@redux-rags` and `@@redux-rags/map` keys in the `dynamicReducers` object. \n\n### injectReducer\nSignature: `(keys: Array<string>, reducer: Function) => void`\n\nThis function will insert the given `reducer` at the nested key location in `dynamicReducers` specified by `keys`. For example, if the user has called `ragFactory` with the name `'user info'` internally we will call this function with `injectReducer(['@@redux-rags', 'user info'], subreducer)`. This will set values so that `dynamicReducers['@@redux-rags']['user info'] == subreducer` and update the store with the newly injected subreducer.\n\nYou can call this function and inject your own subreducers. You could just use `configureRags`, `combineAsyncReducers`, and `injectReducer` to setup your store to allow for dynamic reducers and skip the rest of this module. \n\n### ragFactory\n\nThough both `load` and `update` are optional, you pass at least one. With neither of these, there is no good way to\nupdate the data or metadata fields.\n\nThe `ragFactory` will place the dynamically added subreducers in the `@@redux-rags` top leve of the Redux store.\nIf you want to place this somewhere else, just put the returned `subreducer` somewhere in your Redux store and\npass in the `getInStore` function.\n\n|  Props  |  Type  |  Optional  |  Description  |\n|:-------:|:------:|:----------:|:-------------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to Redux and the automatically injected subreducer will use that name. |\n| load | Function | yes | A function that loads data |\n| getInitialState | Function | yes | Function to create initial data for the return of `load`, will use null if not defined |\n| getInStore | Function | yes | `(state) => state.some.path.to.subreducer` Function to locate the subreducer in the store. Will place in default location if not specified, otherwise will use this location in getters. |\n| update | Function | yes | (dataValue, ...args) => { return newData; } A function to manipulate the data attribute. Returned `update` will have signature `(...args) => void` |\n| loadOnlyOnce | boolean | yes | Prevent the `load` function from being called more than once. |\n| partialReducer | Function | yes | Allows you to hook in to other actions in the Redux store and modify the state. You'll be working with the BoilerState argument. Extend the reducer and listen to other actions, for example you can could clear the data on user logout. Write this like a reducer to extend the functionality of the generated boilerplate. You can also delay assignment if you want to utilize actions in the returned Actions object, just assign the function to `subreducer.partialReducer`.|\n\n### ragFactoryMap\nWhat's this `ragFactoryMap`? Well if you want to cache data based on the query parameters, then the `ragFactoryMap` is for you!\nFor each collection of args, a new reducer will be injected. The params are stringified and used as the key for the injected subreducer.\n\nThe `ragFactoryMap` builds on top of `ragFactory`. That factory is used internally to generate the subreducers and actions for each key.\n\n| Props | Type | Optional | Description |\n|:-----:|:----:|:--------:|:-----------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to Redux and the automatically injected subreducer will use that name. |\n| load | Function | no | A function that loads data. The returned function will have the same signature. |\n| getInitialState | Function | yes | Function to create initial data for the return of load, will use null if not defined |\n\n\n## Additional Examples\n\n### Defining the Redux interactions in a separate file and importing\nWe'll use a basic fetch for this example, hitting some json endpoint. The returned load function\nhas the same signature as the function we passed in, so in this case `loadFaq` will not use\nany parameters.\n```js\nimport { ragFactory } from 'redux-rags';\n\nconst axiosLoadFaq = () => fetch('http://api.example.com/data.json');\nconst { actions, getters } = ragFactory({ load: axiosLoadFaq });\n\nexport {\n  loadFaq: actions.load,\n  getFaq: getters.getData,\n  getIsFaqLoading: getters.getIsLoading,\n};\n```\n\n#### Use that Data Request in a React Component\nImport the Getters and Action as you normally would and use them with `react-redux`. If you're used to\ndefining thunks and getters in a different file and import them, then the code below should look\nremarkably similar to code that you've already seen or even written.\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { loadFaq, getFaq, getIsFaqLoading } from './faqData';\nimport Loading from './Loading';\nimport FaqItem from './FaqItem';\n\nclass Faq extends React.Component {\n  componentDidMount() {\n    this.props.loadFaq();\n  }\n\n  render() {\n    const { isLoading, faqData } = this.props;\n    if (isLoading) {\n      return <Loading />;\n    }\n\n    return (\n      <React.Fragment>\n        {faqData.map((data, index) => <FaqItem key={index} data={data} />}\n      </React.Fragment>\n    )\n  }\n}\n\nconst mapStateFromProps = state => ({\n  faqData: getFaq(state),\n  isLoading: getIsFaqLoading(state)\n});\n\nexport default connect(mapStateFromProps, { loadFaq })(Faq);\n```\n\n### Placing the subreducer in a custom location in Redux\nConnect subreducer to your desired location in the Redux store, and tell the generator\nwhere you put it. Getters are also factories for you. If you don't care where the\nsubreducer lives, don't pass in a `getInStore` method and the generator will place it for you.\nIn this case taking `subreducer` and adding it to the `combineSubreducers` function in the `currentUser` subreducer.\nBut you can place it wherever you want, just update the `getInStore` function passed to `ragFactory`.\n```js\nimport { generator };\nconst { actions, subreducer, getters } = generator({\n  name: 'MY_DATA',\n  load: (param) => methodService.run('fetchSomething', param),\n  getInStore: (store) => store && store.currentUser && store.currentUser.my_data\n});\n```\n\n### Using update to manipulate the stored value\nIn addition to `load` you can also pass in an `update` function, which will take the\ncurrent `data` value as the first parameter. An example of this might be an update\nfunction that increments the current value by 2. The signature of the returned function\nis slightly different, as the `data` first argument is passed internally, so you pass\nin a function that takes parameters `(currentData, ...additionalParams)` and the returned\n`actions.update` function will take the parameters `(...additionalParams)`.\n[View an example that relies entirely on the `update` funciton on CodeSandbox](https://codesandbox.io/s/1307vwj8zj) \n```js\nimport { generator };\nconst { actions : { update }, getters } = generator({\n  name: 'MY_DATA',\n  update: (currentData) => currentData + 2,\n});\n```\n\n### Using the ragFactoryMap\nNot much to say, it's pretty much how you would use `ragFactory`, except treating the input arguments\nas a key for a different mini state machine.\n\n```js\n const { action: { load }, getters: { getData, getMeta } } =\n   ragFactoryMap({ load: Function });\n\n // In a component after `load` was passed in through mapDispatchToProps.\n\nclass Component extends React.Component {\n  componentDidMount() {\n    const { loaded, load, taskId, userId } = this.props;\n    // Load the data for given userId and taskId if not loaded\n    !loaded && load(userId, taskId);\n  }\n  // ... Other functions of the class\n}\n\nfunction mapStateToProps(state, props) {\n  const { userId, taskId } = props;\n  return {\n    loaded: getMeta(userId, taskId).loaded,\n    taskData: getData(userId, taskId)\n  }\n}\nexport default connect(mapStateToProps)(Component);\n```\n\n## License\nMIT\n\n## Thanks\n\nHuge thanks to Jimmy Shen for [the amazing Medium article already linked above](https://medium.com/@jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7)\non dynamically injecting reducers into the Redux store.\n","gitHead":"ef1c89cb7897bc6703752761b7fdc01ed0bfbac6","scripts":{"flow":"flow","jest":"jest --maxWorkers=2","lint":"eslint src/**","test":"jest","build":"babel src -d build --copy-files","jest:watch":"npm run jest -- --watchAll","lint:watch":"esw -w lib/**","prepublish":"npm run build"},"_npmUser":{"name":"kyleatcodesignal","email":"kyle@codesignal.com"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.","repository":{"url":"git+https://github.com/CodeSignal/redux-rags.git","type":"git"},"_npmVersion":"5.6.0","description":"Redux Reducers, Actions, and Getters. Simplified!","directories":{},"_nodeVersion":"8.9.4","_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"jest":"^23.6.0","redux":"^4.0.1","eslint":"^5.8.0","flow-bin":"^0.86.0","babel-cli":"^6.26.0","coveralls":"^3.0.2","babel-core":"^6.26.3","babel-jest":"^23.6.0","eslint-watch":"^4.0.2","babel-preset-env":"^1.7.0","babel-preset-flow":"^6.23.0","regenerator-runtime":"^0.13.1","eslint-plugin-import":"^2.14.0"},"_npmOperationalInternal":{"tmp":"tmp/redux-rags_1.2.0-beta_1548181957251_0.09550237957935925","host":"s3://npm-registry-packages"}},"1.2.0-beta1":{"name":"redux-rags","version":"1.2.0-beta1","keywords":["redux"],"author":{"name":"CodeSignal"},"license":"MIT","_id":"redux-rags@1.2.0-beta1","maintainers":[{"name":"kyleatcodesignal","email":"kyle@codesignal.com"}],"homepage":"https://github.com/CodeSignal/redux-rags#readme","bugs":{"url":"https://github.com/CodeSignal/redux-rags/issues"},"dist":{"shasum":"0fb10ca41000b69ecfe4b68e1e95cc66efbe8edb","tarball":"https://registry.npmjs.org/redux-rags/-/redux-rags-1.2.0-beta1.tgz","fileCount":10,"integrity":"sha512-4DiAiR1DbWtMC0MkiQ3Wmw2Ejy9Ix32NSe++TE4v60PzgfHANnocuLPKhTOqhkPUZJe8FsvmTQvYMXuznMNTIg==","signatures":[{"sig":"MEQCICyrBcCn+XUf60ddZ9HSZWPp3Ir1VpAWxvMJ0DggJvhgAiAEUvaxY6m8USq5uKBHePKkFeYkaPr4JZg/YMnE1Dav1g==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":41035,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcR3ZtCRA9TVsSAnZWagAAaEMQAJRzXhNSEx0N1zLeBs1u\nMVUeDBgKI9AS+0NhCgrZlNqoWUr1nPg2j7pBuZGtRCLDtoMOqONfYsv0hsJj\njVlwc9HFt8mTr+rrZqnAvcJ6z9bmn2Qsqj/EF4UuvzY5KGG/rXkhEVdv+5cs\nW91LY2EexTB30waY90QxV1daEKnC9yJGXSMGhQsynZxC7b+7Gr7oGpD+Wp3Q\ncz5pAkalNFfrIcc2wsTUMspSVcHGEs8SmNRrv3H62oQ3SJNIl6aqbH4UoLJl\n0uRlMERxyxcd3FR1pVqWw6Wdq2XIDhfLlst3XSO70o5XJxX/w22zB+sWThE8\nPvHv/e9ksQXkb8X0niYYSu97EYVTLM0IhfoJVrB18Zi5R8J4eT1mxBJ3prce\nog+3mtCfrAYZtL79mcBzXausE4gVXPMaRz6QMRt5ENMHIogKKzfGl18C7iWa\n79x0dvSyHORze/12p4E4YskCtG3HkmdZyWehey9YbGek/DxfdaNhmrBtSO6I\n9dRPC21nHATo1xZTAydKIuJlneKlOqosX3U/Gkfzd9YlHuMfbES+bv4T+0IY\nqODSDI8R72T6s7Pio2XMnP4cpzAgI9VVtcwPYeSDMzKx9yqG31/3BKWWkZUw\nHMcH+oo1wscj9DI+IR0uTQwytIUISr7HCvOyAKbDKLTaBLPF6G2x8HO4kIVE\nqal/\r\n=Kg/q\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/index.js","readme":"# Redux-RAGs\n[![npm version](https://img.shields.io/npm/v/redux-rags.svg?style=flat-square)](https://www.npmjs.com/package/redux-rags)\n[![npm downloads](https://img.shields.io/npm/dm/redux-rags.svg?style=flat-square)](https://www.npmjs.com/package/redux-rags)\n[![Coverage Status](https://coveralls.io/repos/github/CodeSignal/redux-rags/badge.svg?branch=master)](https://coveralls.io/github/CodeSignal/redux-rags?branch=master)\n\nRedux **R**educers, **A**ctions, and **G**etters: Simplified!\n\nTLDR: No need to create or connect subreducers for simple async requests. Your Redux connection is handled by a single function\n```js\nconst rag = ragFactory({ load });\n\nconst newLoad = rag.actions.load;\nconst getData = rag.getters.getData;\nconnect(state => ({ data: getData(store) }), { load: newLoad })(Component);\n```\n\n## Motivation\nWe found that a lot of our reducers were redefining a common theme. We have some endpoint\nthat we want to query for data. We'd run through the same steps every time:\n* Begin loading state\n* Fetch Data\n* End Loading State\n* Set data or error\n\n<center>\n  <img src=\"media/data-flow.png\" width=\"80%\" />\n</center>\n\nAnd that's it: A mini state machine that we redefined over and over, creating dozens of\nsimilar actions and subreducers. But what if there were an easier way? What if we could\ndefine that mini state machine once and re-use the logic with each kind of query? Well `redux-rags` is\nhere to help!\n\n## Injecting a Subreducer?!\nUsing Redux requires quite a lot of boilerplate. You need to create Actions and maybe Action Creators. You need to create Reducers and place them somewhere in the store, maybe as a part of another reducer. There are a myriad of tools out there to help you simplify this process, and we're adding another to the list! This one is built around the idea that there are some pieces of state that you would like to have stored outside of a particular rendering context, but don't want to go through all the migration work to create a custom reducer in the store for this data. You want to take advantage of the benefits of Redux without trudging through all the boilerplate.\n\nSo what if you had a way to add subreducers to Redux? Then you could write code that generates the subreducer structures you use frequently and inject them! You could now start writing subreducers aimed at general cases of data interaction and vastly reduce the time it takes to migrate a piece of state to Redux. We've provided our most useful abstraction here, a simple data request lifecycle. Hopefully you can see the opportunities that injecting a subreducer provides and can use the code here as an example for how to start reusing your own subreducer logic.\n\nFor more details, I encourage you to check out [this Medium article by Jimmy Shen on subreducer injection.](https://medium.com/@jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7)\n\n## Usage\n\n\n### Single File Redux Connection Example\nHere's how you'd interact with Redux for a data request. We're going to create the actions and\nautomatically create and inject the subreducer. The returned `load` function will take the same\narguments as the `load` function passed in, so you have control over the `load` thunk.\n\nHere we want to hit an endpoint with the `userId` parameter. We'll avoid worrying about\nloading states or anything for now. The call to `ragFactory` creates the actions and injects\nthe subreducer for us. Then this state information is stored in Redux, so when users return to the\ncomponent they'll see the cached data. [Play with a similar example on CodeSandbox](https://codesandbox.io/s/k218x138l7)\n\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { ragFactory } from 'redux-rags';\n\n// Pass load to factory, factory returns basically the same load that we can give to connect.\nconst { actions: { load }, getters: { getData } } = ragFactory({\n  load: (userId) => axios.get('/users', { userId })\n});\n\nfunction ViewUser({userId, load, user}) {\n  return (\n    <div>\n      <button onClick={() => load(userId)}>Load User Info</button>\n      {user && <UserData data={user} />}\n    </div>\n  )\n}\n\nexport default connect(state => ({ user: getData(state)}), { load })(ViewUser)\n```\n\n\nAnd that's it! That's all you need to do; no need to manually create or place a subreducer\nanywhere. The subreducer is pretty basic, it holds the return of `load` in the data attribute.\nIt also keeps track of meta information, like last load time, number of times loaded, and errors.\n\nIf you want to keep track of data by parameters passed to `load`, i.e. loading a whole bunch\nof users, you'll want to use `ragFactoryMap`. This will use `ragFactory` behind the scenes to\ncreate the simple mini-reducer for each set of parameters you specify.\n\n\n## Prerequisites\nYou'll need `redux-thunk` and to restructure your `createRootReducer` function. We'll\nneed to handle the addition of dynamic reducers!\n\nHere's what your Redux store creation will look like:\n```js\nimport { combineReducers, createStore, applyMiddleware } from 'redux';\nimport thunk from 'redux-thunk';\n\nimport { combineAsyncReducers, configureRags } from 'redux-rags';\n\n// Import a few reducers like you normally would to create a top level reducer: we'll use `userReducer` as an example\nimport userReducer from './userReducer';\n\nconst createRootReducer = (dynamicReducers: Object = {}) => {\n  dynamicReducers = combineAsyncReducers(combineReducers, dynamicReducers);\n\n  return combineReducers(Object.assign({}, dynamicReducers, {\n    // Then list your reducers below\n    userReducer\n  }));\n}\n\nconst middleware = applyMiddleware(thunk);\nconst store = createStore(createRootReducer(), compose(middleware));\nconfigureRags(store, createRootReducer);\n```\n\n## What Actions and Getters are there?\nGetters take in the full store as the argument. For `ragFactory`, you'll have access to the following getters:\n\n- `get`: Returns `BoilerState<T>`, so an object that looks like `{ data, meta }`.\n- `getData`: Returns the `data` attribute from `BoilerState<T>`. It would be type `?T`, whatever your load / update functions passed to the `ragFactory` return.\n- `getMeta`: Returns the `meta` attribute from `BoilerState<T>`. This will be an object that looks like `{ loading, lastLoadTime, errors }`, with a few more properties. Look for the types / additional properties in the type definition.\n- `getIsLoading`: Returns the `loading` attribute from the `meta` field. A common use case is for quick access to the loading state, so a special getter is provided for this meta attribute.\n\nActions are implementations of commonly used features to manipulate the state. Most of these are action creators, with `load` and `update` being thunks.\n\n- `beginLoading`: Sets `loading` meta property to true.\n- `endLoading`: Sets `loading` meta property to false.\n- `reset`: Returns the subreducer to the initial state.\n- `errors`: Sets the errors meta value.\n- `clearErrors`: Clears the errors meta value.\n- `updateData`: Sets the data value for the subreducer.\n- `update`: Thunk. Calls the `update` function passed in to `ragsFactory` and sets the data value to the result. Might also set the error attribute if the `update` function throws an error. Passed in function should look like: `(dataValue, ...params) => newData`. Returned func has signature: `(...params) => newData`. This is because the current data value will be added to the function internally.\n- `load`: Thunk. Calls the appropriate sequence of `beginLoading`, `updateData`, `endLoading` actions while calling the `load` function passed to `ragsFactory`. If there are errors while executing, the error meta value will be set. Passed in function looks like: `(...params) => newData`, returned function looks like `(...params) => newData`.\n\n## Details and explanations of exports\n\n### A few types to consider:\nThe generated subreducer uses the following type for the state:\n```js\ntype BoilerState<T> = {\n  data: ?T,\n  meta: {\n    loading: boolean,\n    loaded: boolean,\n    changeCount: number,\n    lastChangeTime: ?number,\n    errors?: *\n  }\n}\n```\n|  Meta Property | Description |\n|:----:|:---:|\n| loading | Is the data currently being loaded |\n| loaded | Has the data every been loaded? Equivalent to `changeCount > 0` |\n| changeCount | Number of times the data has been loaded / updated |\n| lastChangeTime | The time of the last change |\n| errors | Error object from the `load` or `update` function, if any |\n\n### configureRags\nSignature: `(store: Object, createRootReducer: Function) => void`.\n\nThis function is how you set up the module, giving it access to your Redux store. This is used to configure the reducer\ninjector. We need `store` for the [`replaceReducer`](https://redux.js.org/api/store#replaceReducer) method. And we'll need to have a formatted `createRootReducer` function that accepts dynamic reducers. We've provided a `combineAsyncReducers` function to make this easier.\n\n### combineAsyncReducers\nSignature: `(combineReducers: Function, dynamicReducers: Object) => newDynamicReducers`\n\nThis function is designed to recursively call the `combineReducers` function on the second parameter `dynamicReducers`. This means that whatever the structure of `dynamicReducers` we can register the functions with `redux`. The package internally will add values underneath `@@redux-rags` and `@@redux-rags/map` keys in the `dynamicReducers` object. \n\n### injectReducer\nSignature: `(keys: Array<string>, reducer: Function) => void`\n\nThis function will insert the given `reducer` at the nested key location in `dynamicReducers` specified by `keys`. For example, if the user has called `ragFactory` with the name `'user info'` internally we will call this function with `injectReducer(['@@redux-rags', 'user info'], subreducer)`. This will set values so that `dynamicReducers['@@redux-rags']['user info'] == subreducer` and update the store with the newly injected subreducer.\n\nYou can call this function and inject your own subreducers. You could just use `configureRags`, `combineAsyncReducers`, and `injectReducer` to setup your store to allow for dynamic reducers and skip the rest of this module. \n\n### ragFactory\n\nThough both `load` and `update` are optional, you pass at least one. With neither of these, there is no good way to\nupdate the data or metadata fields.\n\nThe `ragFactory` will place the dynamically added subreducers in the `@@redux-rags` top leve of the Redux store.\nIf you want to place this somewhere else, just put the returned `subreducer` somewhere in your Redux store and\npass in the `getInStore` function.\n\n|  Props  |  Type  |  Optional  |  Description  |\n|:-------:|:------:|:----------:|:-------------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to Redux and the automatically injected subreducer will use that name. |\n| load | Function | yes | A function that loads data |\n| getInitialState | Function | yes | Function to create initial data for the return of `load`, will use null if not defined |\n| getInStore | Function | yes | `(state) => state.some.path.to.subreducer` Function to locate the subreducer in the store. Will place in default location if not specified, otherwise will use this location in getters. |\n| update | Function | yes | (dataValue, ...args) => { return newData; } A function to manipulate the data attribute. Returned `update` will have signature `(...args) => void` |\n| loadOnlyOnce | boolean | yes | Prevent the `load` function from being called more than once. |\n| partialReducer | Function | yes | Allows you to hook in to other actions in the Redux store and modify the state. You'll be working with the BoilerState argument. Extend the reducer and listen to other actions, for example you can could clear the data on user logout. Write this like a reducer to extend the functionality of the generated boilerplate. You can also delay assignment if you want to utilize actions in the returned Actions object, just assign the function to `subreducer.partialReducer`.|\n\n### ragFactoryMap\nWhat's this `ragFactoryMap`? Well if you want to cache data based on the query parameters, then the `ragFactoryMap` is for you!\nFor each collection of args, a new reducer will be injected. The params are stringified and used as the key for the injected subreducer.\n\nThe `ragFactoryMap` builds on top of `ragFactory`. That factory is used internally to generate the subreducers and actions for each key.\n\n| Props | Type | Optional | Description |\n|:-----:|:----:|:--------:|:-----------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to Redux and the automatically injected subreducer will use that name. |\n| load | Function | no | A function that loads data. The returned function will have the same signature. |\n| getInitialState | Function | yes | Function to create initial data for the return of load, will use null if not defined |\n\n\n## Additional Examples\n\n### Defining the Redux interactions in a separate file and importing\nWe'll use a basic fetch for this example, hitting some json endpoint. The returned load function\nhas the same signature as the function we passed in, so in this case `loadFaq` will not use\nany parameters.\n```js\nimport { ragFactory } from 'redux-rags';\n\nconst axiosLoadFaq = () => fetch('http://api.example.com/data.json');\nconst { actions, getters } = ragFactory({ load: axiosLoadFaq });\n\nexport {\n  loadFaq: actions.load,\n  getFaq: getters.getData,\n  getIsFaqLoading: getters.getIsLoading,\n};\n```\n\n#### Use that Data Request in a React Component\nImport the Getters and Action as you normally would and use them with `react-redux`. If you're used to\ndefining thunks and getters in a different file and import them, then the code below should look\nremarkably similar to code that you've already seen or even written.\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { loadFaq, getFaq, getIsFaqLoading } from './faqData';\nimport Loading from './Loading';\nimport FaqItem from './FaqItem';\n\nclass Faq extends React.Component {\n  componentDidMount() {\n    this.props.loadFaq();\n  }\n\n  render() {\n    const { isLoading, faqData } = this.props;\n    if (isLoading) {\n      return <Loading />;\n    }\n\n    return (\n      <React.Fragment>\n        {faqData.map((data, index) => <FaqItem key={index} data={data} />}\n      </React.Fragment>\n    )\n  }\n}\n\nconst mapStateFromProps = state => ({\n  faqData: getFaq(state),\n  isLoading: getIsFaqLoading(state)\n});\n\nexport default connect(mapStateFromProps, { loadFaq })(Faq);\n```\n\n### Placing the subreducer in a custom location in Redux\nConnect subreducer to your desired location in the Redux store, and tell the generator\nwhere you put it. Getters are also factories for you. If you don't care where the\nsubreducer lives, don't pass in a `getInStore` method and the generator will place it for you.\nIn this case taking `subreducer` and adding it to the `combineSubreducers` function in the `currentUser` subreducer.\nBut you can place it wherever you want, just update the `getInStore` function passed to `ragFactory`.\n```js\nimport { generator };\nconst { actions, subreducer, getters } = generator({\n  name: 'MY_DATA',\n  load: (param) => methodService.run('fetchSomething', param),\n  getInStore: (store) => store && store.currentUser && store.currentUser.my_data\n});\n```\n\n### Using update to manipulate the stored value\nIn addition to `load` you can also pass in an `update` function, which will take the\ncurrent `data` value as the first parameter. An example of this might be an update\nfunction that increments the current value by 2. The signature of the returned function\nis slightly different, as the `data` first argument is passed internally, so you pass\nin a function that takes parameters `(currentData, ...additionalParams)` and the returned\n`actions.update` function will take the parameters `(...additionalParams)`.\n[View an example that relies entirely on the `update` funciton on CodeSandbox](https://codesandbox.io/s/1307vwj8zj) \n```js\nimport { generator };\nconst { actions : { update }, getters } = generator({\n  name: 'MY_DATA',\n  update: (currentData) => currentData + 2,\n});\n```\n\n### Using the ragFactoryMap\nNot much to say, it's pretty much how you would use `ragFactory`, except treating the input arguments\nas a key for a different mini state machine.\n\n```js\n const { action: { load }, getters: { getData, getMeta } } =\n   ragFactoryMap({ load: Function });\n\n // In a component after `load` was passed in through mapDispatchToProps.\n\nclass Component extends React.Component {\n  componentDidMount() {\n    const { loaded, load, taskId, userId } = this.props;\n    // Load the data for given userId and taskId if not loaded\n    !loaded && load(userId, taskId);\n  }\n  // ... Other functions of the class\n}\n\nfunction mapStateToProps(state, props) {\n  const { userId, taskId } = props;\n  return {\n    loaded: getMeta(userId, taskId).loaded,\n    taskData: getData(userId, taskId)\n  }\n}\nexport default connect(mapStateToProps)(Component);\n```\n\n## License\nMIT\n\n## Thanks\n\nHuge thanks to Jimmy Shen for [the amazing Medium article already linked above](https://medium.com/@jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7)\non dynamically injecting reducers into the Redux store.\n","gitHead":"69ccf99bc5c8a8fa51f074621bf0bf8f67a228dc","scripts":{"flow":"flow","jest":"jest --maxWorkers=2","lint":"eslint src/**","test":"jest","build":"babel src -d build --copy-files","jest:watch":"npm run jest -- --watchAll","lint:watch":"esw -w lib/**","prepublish":"npm run build"},"_npmUser":{"name":"kyleatcodesignal","email":"kyle@codesignal.com"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.","repository":{"url":"git+https://github.com/CodeSignal/redux-rags.git","type":"git"},"_npmVersion":"5.6.0","description":"Redux Reducers, Actions, and Getters. Simplified!","directories":{},"_nodeVersion":"8.9.4","_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"jest":"^23.6.0","redux":"^4.0.1","eslint":"^5.8.0","flow-bin":"^0.86.0","coveralls":"^3.0.2","@babel/cli":"^7.2.3","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","@babel/core":"^7.2.2","babel-eslint":"^10.0.1","eslint-watch":"^4.0.2","babel-polyfill":"^6.26.0","@babel/register":"^7.0.0","@babel/preset-env":"^7.3.1","@babel/preset-flow":"^7.0.0","regenerator-runtime":"^0.13.1","eslint-plugin-import":"^2.14.0","babel-plugin-transform-imports":"^1.5.1","@babel/plugin-transform-modules-commonjs":"^7.2.0","@babel/plugin-proposal-object-rest-spread":"^7.3.1"},"_npmOperationalInternal":{"tmp":"tmp/redux-rags_1.2.0-beta1_1548187244742_0.8148951546693373","host":"s3://npm-registry-packages"}},"1.2.0-beta2":{"name":"redux-rags","version":"1.2.0-beta2","keywords":["redux"],"author":{"name":"CodeSignal"},"license":"MIT","_id":"redux-rags@1.2.0-beta2","maintainers":[{"name":"kyleatcodesignal","email":"kyle@codesignal.com"}],"homepage":"https://github.com/CodeSignal/redux-rags#readme","bugs":{"url":"https://github.com/CodeSignal/redux-rags/issues"},"dist":{"shasum":"06cea4090eab6d66aef00a6ae93f86ecb56fb66d","tarball":"https://registry.npmjs.org/redux-rags/-/redux-rags-1.2.0-beta2.tgz","fileCount":10,"integrity":"sha512-SaTx9ktfiBhNvOik6fGvyzCHabEj8YCIsTRSpKFvv+MM5sz7HnHjKXkjcaDGUVk5hAKWTJ5q9g5yPNlZzZUtHg==","signatures":[{"sig":"MEUCIAtpx75rjPxiYe7lmICd9Kh5AzZrDaMzgZSguDsvw7tNAiEA3NLvw0yEjIIaT1T1PGpKLLhIjqiDQedoYbARhwpiMb8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":41825,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcR3pECRA9TVsSAnZWagAAgSgP/jC6MYeH32HBdGxAYBWg\nZbt66grLRbeLYGrTnW6ysTtCdSFbHMT/bkgYbSQA1n0OJiwltFwibDsJwW1i\nYBeXynEOJV8eaGRHp9dmyBIaumEGsgAKB1WK0KEZmo6aGGUHlM42uUSi40JX\nGkMKUxsksMsGtcLu8IW3GhTdRj9v/Q8si1GPKQlSnpn+48EhljHN7xLxs3JO\nbULJjGJ6FcOvkXdlR15wl+JBsNkFpsZ0Gm1uBIKthy8piqqxDnoxPyjai3+q\nbpstN012Lqrb4tEsYwOPFqKkUsizU1XRgTgyB2IRmuxSLExu2eYhNlu/OnC/\nz86NNItCY/fM6IHOn4hnG4mg89d0V0aNId+iTA+kVwJKl+IWUZJDCUKiyi2T\ntpr1Jz3rI4TtB2nCNv1as0GhYYDiDH3AKtCQ+7Yl5VnROZSKyUbY4XK1vRNM\nZ1oDx1OI1Q9pGY81+h5DqmwzTcXiN3xXAACTTk4E6wO3E/qijvs3Px67QE8r\nnhZsfJIHVZI8UUW5hZM5HXhMc48zgH9ibUQDxevdovQqguSeYv4V2UNfb/SW\nWeOZl7Y9kS7ZOd0lt7EkSSSLzNNhxJChXlvz3XpbTBWkOBfh6SN8decziF4C\ncXIcjWdw1CfGcxJeRJkHOQYQmkpvRj+wOHDu0SzctFKa/+OyXbWnMYRxXzcZ\nsi5u\r\n=Ur1g\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/index.js","readme":"# Redux-RAGs\n[![npm version](https://img.shields.io/npm/v/redux-rags.svg?style=flat-square)](https://www.npmjs.com/package/redux-rags)\n[![npm downloads](https://img.shields.io/npm/dm/redux-rags.svg?style=flat-square)](https://www.npmjs.com/package/redux-rags)\n[![Coverage Status](https://coveralls.io/repos/github/CodeSignal/redux-rags/badge.svg?branch=master)](https://coveralls.io/github/CodeSignal/redux-rags?branch=master)\n\nRedux **R**educers, **A**ctions, and **G**etters: Simplified!\n\nTLDR: No need to create or connect subreducers for simple async requests. Your Redux connection is handled by a single function\n```js\nconst rag = ragFactory({ load });\n\nconst newLoad = rag.actions.load;\nconst getData = rag.getters.getData;\nconnect(state => ({ data: getData(store) }), { load: newLoad })(Component);\n```\n\n## Motivation\nWe found that a lot of our reducers were redefining a common theme. We have some endpoint\nthat we want to query for data. We'd run through the same steps every time:\n* Begin loading state\n* Fetch Data\n* End Loading State\n* Set data or error\n\n<center>\n  <img src=\"media/data-flow.png\" width=\"80%\" />\n</center>\n\nAnd that's it: A mini state machine that we redefined over and over, creating dozens of\nsimilar actions and subreducers. But what if there were an easier way? What if we could\ndefine that mini state machine once and re-use the logic with each kind of query? Well `redux-rags` is\nhere to help!\n\n## Injecting a Subreducer?!\nUsing Redux requires quite a lot of boilerplate. You need to create Actions and maybe Action Creators. You need to create Reducers and place them somewhere in the store, maybe as a part of another reducer. There are a myriad of tools out there to help you simplify this process, and we're adding another to the list! This one is built around the idea that there are some pieces of state that you would like to have stored outside of a particular rendering context, but don't want to go through all the migration work to create a custom reducer in the store for this data. You want to take advantage of the benefits of Redux without trudging through all the boilerplate.\n\nSo what if you had a way to add subreducers to Redux? Then you could write code that generates the subreducer structures you use frequently and inject them! You could now start writing subreducers aimed at general cases of data interaction and vastly reduce the time it takes to migrate a piece of state to Redux. We've provided our most useful abstraction here, a simple data request lifecycle. Hopefully you can see the opportunities that injecting a subreducer provides and can use the code here as an example for how to start reusing your own subreducer logic.\n\nFor more details, I encourage you to check out [this Medium article by Jimmy Shen on subreducer injection.](https://medium.com/@jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7)\n\n## Usage\n\n\n### Single File Redux Connection Example\nHere's how you'd interact with Redux for a data request. We're going to create the actions and\nautomatically create and inject the subreducer. The returned `load` function will take the same\narguments as the `load` function passed in, so you have control over the `load` thunk.\n\nHere we want to hit an endpoint with the `userId` parameter. We'll avoid worrying about\nloading states or anything for now. The call to `ragFactory` creates the actions and injects\nthe subreducer for us. Then this state information is stored in Redux, so when users return to the\ncomponent they'll see the cached data. [Play with a similar example on CodeSandbox](https://codesandbox.io/s/k218x138l7)\n\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { ragFactory } from 'redux-rags';\n\n// Pass load to factory, factory returns basically the same load that we can give to connect.\nconst { actions: { load }, getters: { getData } } = ragFactory({\n  load: (userId) => axios.get('/users', { userId })\n});\n\nfunction ViewUser({userId, load, user}) {\n  return (\n    <div>\n      <button onClick={() => load(userId)}>Load User Info</button>\n      {user && <UserData data={user} />}\n    </div>\n  )\n}\n\nexport default connect(state => ({ user: getData(state)}), { load })(ViewUser)\n```\n\n\nAnd that's it! That's all you need to do; no need to manually create or place a subreducer\nanywhere. The subreducer is pretty basic, it holds the return of `load` in the data attribute.\nIt also keeps track of meta information, like last load time, number of times loaded, and errors.\n\nIf you want to keep track of data by parameters passed to `load`, i.e. loading a whole bunch\nof users, you'll want to use `ragFactoryMap`. This will use `ragFactory` behind the scenes to\ncreate the simple mini-reducer for each set of parameters you specify.\n\n\n## Prerequisites\nYou'll need `redux-thunk` and to restructure your `createRootReducer` function. We'll\nneed to handle the addition of dynamic reducers!\n\nHere's what your Redux store creation will look like:\n```js\nimport { combineReducers, createStore, applyMiddleware } from 'redux';\nimport thunk from 'redux-thunk';\n\nimport { combineAsyncReducers, configureRags } from 'redux-rags';\n\n// Import a few reducers like you normally would to create a top level reducer: we'll use `userReducer` as an example\nimport userReducer from './userReducer';\n\nconst createRootReducer = (dynamicReducers: Object = {}) => {\n  dynamicReducers = combineAsyncReducers(combineReducers, dynamicReducers);\n\n  return combineReducers(Object.assign({}, dynamicReducers, {\n    // Then list your reducers below\n    userReducer\n  }));\n}\n\nconst middleware = applyMiddleware(thunk);\nconst store = createStore(createRootReducer(), compose(middleware));\nconfigureRags(store, createRootReducer);\n```\n\n## What Actions and Getters are there?\nGetters take in the full store as the argument. For `ragFactory`, you'll have access to the following getters:\n\n- `get`: Returns `BoilerState<T>`, so an object that looks like `{ data, meta }`.\n- `getData`: Returns the `data` attribute from `BoilerState<T>`. It would be type `?T`, whatever your load / update functions passed to the `ragFactory` return.\n- `getMeta`: Returns the `meta` attribute from `BoilerState<T>`. This will be an object that looks like `{ loading, lastLoadTime, errors }`, with a few more properties. Look for the types / additional properties in the type definition.\n- `getIsLoading`: Returns the `loading` attribute from the `meta` field. A common use case is for quick access to the loading state, so a special getter is provided for this meta attribute.\n\nActions are implementations of commonly used features to manipulate the state. Most of these are action creators, with `load` and `update` being thunks.\n\n- `beginLoading`: Sets `loading` meta property to true.\n- `endLoading`: Sets `loading` meta property to false.\n- `reset`: Returns the subreducer to the initial state.\n- `errors`: Sets the errors meta value.\n- `clearErrors`: Clears the errors meta value.\n- `updateData`: Sets the data value for the subreducer.\n- `update`: Thunk. Calls the `update` function passed in to `ragsFactory` and sets the data value to the result. Might also set the error attribute if the `update` function throws an error. Passed in function should look like: `(dataValue, ...params) => newData`. Returned func has signature: `(...params) => newData`. This is because the current data value will be added to the function internally.\n- `load`: Thunk. Calls the appropriate sequence of `beginLoading`, `updateData`, `endLoading` actions while calling the `load` function passed to `ragsFactory`. If there are errors while executing, the error meta value will be set. Passed in function looks like: `(...params) => newData`, returned function looks like `(...params) => newData`.\n\n## Details and explanations of exports\n\n### A few types to consider:\nThe generated subreducer uses the following type for the state:\n```js\ntype BoilerState<T> = {\n  data: ?T,\n  meta: {\n    loading: boolean,\n    loaded: boolean,\n    changeCount: number,\n    lastChangeTime: ?number,\n    errors?: *\n  }\n}\n```\n|  Meta Property | Description |\n|:----:|:---:|\n| loading | Is the data currently being loaded |\n| loaded | Has the data every been loaded? Equivalent to `changeCount > 0` |\n| changeCount | Number of times the data has been loaded / updated |\n| lastChangeTime | The time of the last change |\n| errors | Error object from the `load` or `update` function, if any |\n\n### configureRags\nSignature: `(store: Object, createRootReducer: Function) => void`.\n\nThis function is how you set up the module, giving it access to your Redux store. This is used to configure the reducer\ninjector. We need `store` for the [`replaceReducer`](https://redux.js.org/api/store#replaceReducer) method. And we'll need to have a formatted `createRootReducer` function that accepts dynamic reducers. We've provided a `combineAsyncReducers` function to make this easier.\n\n### combineAsyncReducers\nSignature: `(combineReducers: Function, dynamicReducers: Object) => newDynamicReducers`\n\nThis function is designed to recursively call the `combineReducers` function on the second parameter `dynamicReducers`. This means that whatever the structure of `dynamicReducers` we can register the functions with `redux`. The package internally will add values underneath `@@redux-rags` and `@@redux-rags/map` keys in the `dynamicReducers` object. \n\n### injectReducer\nSignature: `(keys: Array<string>, reducer: Function) => void`\n\nThis function will insert the given `reducer` at the nested key location in `dynamicReducers` specified by `keys`. For example, if the user has called `ragFactory` with the name `'user info'` internally we will call this function with `injectReducer(['@@redux-rags', 'user info'], subreducer)`. This will set values so that `dynamicReducers['@@redux-rags']['user info'] == subreducer` and update the store with the newly injected subreducer.\n\nYou can call this function and inject your own subreducers. You could just use `configureRags`, `combineAsyncReducers`, and `injectReducer` to setup your store to allow for dynamic reducers and skip the rest of this module. \n\n### ragFactory\n\nThough both `load` and `update` are optional, you pass at least one. With neither of these, there is no good way to\nupdate the data or metadata fields.\n\nThe `ragFactory` will place the dynamically added subreducers in the `@@redux-rags` top leve of the Redux store.\nIf you want to place this somewhere else, just put the returned `subreducer` somewhere in your Redux store and\npass in the `getInStore` function.\n\n|  Props  |  Type  |  Optional  |  Description  |\n|:-------:|:------:|:----------:|:-------------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to Redux and the automatically injected subreducer will use that name. |\n| load | Function | yes | A function that loads data |\n| getInitialState | Function | yes | Function to create initial data for the return of `load`, will use null if not defined |\n| getInStore | Function | yes | `(state) => state.some.path.to.subreducer` Function to locate the subreducer in the store. Will place in default location if not specified, otherwise will use this location in getters. |\n| update | Function | yes | (dataValue, ...args) => { return newData; } A function to manipulate the data attribute. Returned `update` will have signature `(...args) => void` |\n| loadOnlyOnce | boolean | yes | Prevent the `load` function from being called more than once. |\n| partialReducer | Function | yes | Allows you to hook in to other actions in the Redux store and modify the state. You'll be working with the BoilerState argument. Extend the reducer and listen to other actions, for example you can could clear the data on user logout. Write this like a reducer to extend the functionality of the generated boilerplate. You can also delay assignment if you want to utilize actions in the returned Actions object, just assign the function to `subreducer.partialReducer`.|\n\n### ragFactoryMap\nWhat's this `ragFactoryMap`? Well if you want to cache data based on the query parameters, then the `ragFactoryMap` is for you!\nFor each collection of args, a new reducer will be injected. The params are stringified and used as the key for the injected subreducer.\n\nThe `ragFactoryMap` builds on top of `ragFactory`. That factory is used internally to generate the subreducers and actions for each key.\n\n| Props | Type | Optional | Description |\n|:-----:|:----:|:--------:|:-----------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to Redux and the automatically injected subreducer will use that name. |\n| load | Function | no | A function that loads data. The returned function will have the same signature. |\n| getInitialState | Function | yes | Function to create initial data for the return of load, will use null if not defined |\n\n\n## Additional Examples\n\n### Defining the Redux interactions in a separate file and importing\nWe'll use a basic fetch for this example, hitting some json endpoint. The returned load function\nhas the same signature as the function we passed in, so in this case `loadFaq` will not use\nany parameters.\n```js\nimport { ragFactory } from 'redux-rags';\n\nconst axiosLoadFaq = () => fetch('http://api.example.com/data.json');\nconst { actions, getters } = ragFactory({ load: axiosLoadFaq });\n\nexport {\n  loadFaq: actions.load,\n  getFaq: getters.getData,\n  getIsFaqLoading: getters.getIsLoading,\n};\n```\n\n#### Use that Data Request in a React Component\nImport the Getters and Action as you normally would and use them with `react-redux`. If you're used to\ndefining thunks and getters in a different file and import them, then the code below should look\nremarkably similar to code that you've already seen or even written.\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { loadFaq, getFaq, getIsFaqLoading } from './faqData';\nimport Loading from './Loading';\nimport FaqItem from './FaqItem';\n\nclass Faq extends React.Component {\n  componentDidMount() {\n    this.props.loadFaq();\n  }\n\n  render() {\n    const { isLoading, faqData } = this.props;\n    if (isLoading) {\n      return <Loading />;\n    }\n\n    return (\n      <React.Fragment>\n        {faqData.map((data, index) => <FaqItem key={index} data={data} />}\n      </React.Fragment>\n    )\n  }\n}\n\nconst mapStateFromProps = state => ({\n  faqData: getFaq(state),\n  isLoading: getIsFaqLoading(state)\n});\n\nexport default connect(mapStateFromProps, { loadFaq })(Faq);\n```\n\n### Placing the subreducer in a custom location in Redux\nConnect subreducer to your desired location in the Redux store, and tell the generator\nwhere you put it. Getters are also factories for you. If you don't care where the\nsubreducer lives, don't pass in a `getInStore` method and the generator will place it for you.\nIn this case taking `subreducer` and adding it to the `combineSubreducers` function in the `currentUser` subreducer.\nBut you can place it wherever you want, just update the `getInStore` function passed to `ragFactory`.\n```js\nimport { generator };\nconst { actions, subreducer, getters } = generator({\n  name: 'MY_DATA',\n  load: (param) => methodService.run('fetchSomething', param),\n  getInStore: (store) => store && store.currentUser && store.currentUser.my_data\n});\n```\n\n### Using update to manipulate the stored value\nIn addition to `load` you can also pass in an `update` function, which will take the\ncurrent `data` value as the first parameter. An example of this might be an update\nfunction that increments the current value by 2. The signature of the returned function\nis slightly different, as the `data` first argument is passed internally, so you pass\nin a function that takes parameters `(currentData, ...additionalParams)` and the returned\n`actions.update` function will take the parameters `(...additionalParams)`.\n[View an example that relies entirely on the `update` funciton on CodeSandbox](https://codesandbox.io/s/1307vwj8zj) \n```js\nimport { generator };\nconst { actions : { update }, getters } = generator({\n  name: 'MY_DATA',\n  update: (currentData) => currentData + 2,\n});\n```\n\n### Using the ragFactoryMap\nNot much to say, it's pretty much how you would use `ragFactory`, except treating the input arguments\nas a key for a different mini state machine.\n\n```js\n const { action: { load }, getters: { getData, getMeta } } =\n   ragFactoryMap({ load: Function });\n\n // In a component after `load` was passed in through mapDispatchToProps.\n\nclass Component extends React.Component {\n  componentDidMount() {\n    const { loaded, load, taskId, userId } = this.props;\n    // Load the data for given userId and taskId if not loaded\n    !loaded && load(userId, taskId);\n  }\n  // ... Other functions of the class\n}\n\nfunction mapStateToProps(state, props) {\n  const { userId, taskId } = props;\n  return {\n    loaded: getMeta(userId, taskId).loaded,\n    taskData: getData(userId, taskId)\n  }\n}\nexport default connect(mapStateToProps)(Component);\n```\n\n## License\nMIT\n\n## Thanks\n\nHuge thanks to Jimmy Shen for [the amazing Medium article already linked above](https://medium.com/@jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7)\non dynamically injecting reducers into the Redux store.\n","gitHead":"54f890f05faf755321f11f3809ed0a62a5e149a3","scripts":{"flow":"flow","jest":"jest --maxWorkers=2","lint":"eslint src/**","test":"npm run jest -- --coverage --coverageReporters=text-lcov | ./node_modules/coveralls/bin/coveralls.js","build":"babel src -d build --copy-files","jest:watch":"npm run jest -- --watchAll","lint:watch":"esw -w lib/**","prepublish":"npm run build"},"_npmUser":{"name":"kyleatcodesignal","email":"kyle@codesignal.com"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.","repository":{"url":"git+https://github.com/CodeSignal/redux-rags.git","type":"git"},"_npmVersion":"5.6.0","description":"Redux Reducers, Actions, and Getters. Simplified!","directories":{},"_nodeVersion":"8.9.4","_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"jest":"^23.6.0","redux":"^4.0.1","eslint":"^5.8.0","flow-bin":"^0.86.0","coveralls":"^3.0.2","@babel/cli":"^7.2.3","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","@babel/core":"^7.2.2","babel-eslint":"^10.0.1","eslint-watch":"^4.0.2","babel-polyfill":"^6.26.0","@babel/register":"^7.0.0","@babel/preset-env":"^7.3.1","@babel/preset-flow":"^7.0.0","regenerator-runtime":"^0.13.1","eslint-plugin-import":"^2.14.0","babel-plugin-transform-imports":"^1.5.1","@babel/plugin-transform-modules-commonjs":"^7.2.0","@babel/plugin-proposal-object-rest-spread":"^7.3.1"},"_npmOperationalInternal":{"tmp":"tmp/redux-rags_1.2.0-beta2_1548188228149_0.7707966231807439","host":"s3://npm-registry-packages"}},"1.2.0":{"name":"redux-rags","version":"1.2.0","keywords":["redux"],"author":{"name":"CodeSignal"},"license":"MIT","_id":"redux-rags@1.2.0","maintainers":[{"name":"kyleatcodesignal","email":"kyle@codesignal.com"}],"homepage":"https://github.com/CodeSignal/redux-rags#readme","bugs":{"url":"https://github.com/CodeSignal/redux-rags/issues"},"dist":{"shasum":"208da8b0eed6ddfcb096e86595a29595ac4a5ad2","tarball":"https://registry.npmjs.org/redux-rags/-/redux-rags-1.2.0.tgz","fileCount":10,"integrity":"sha512-KWZlbuUDMVr2rvEQFNFvhT8x8J3eb4DzKVGzPEbvi65FTGXAg2virrhugM3l3KAyPUh/0NbzgmhCsWCcID3Nqw==","signatures":[{"sig":"MEUCIHK0GG0sE6P5phQWO4bad5TYOGS7GgdEj3/YNC+s7he3AiEAp8ZWrvtJNDaMOWU2mPD6p2zDpxlS16P8wS9RU78haxk=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":41819,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcR3vrCRA9TVsSAnZWagAAGuwP/RSCdj8LsbyF8NEnLhVd\nK92g2R+p5ilsxT/Aad0hoO19nkuzfQ0QTbcCd9LnLpTmbNC/yAQz3R71anev\nbA80vX9d96S7s7xus8ClGWPl0zyoDQzPxnPmBTZsXyTguPxHQk3q3UqYfD3x\nK9JMzt/PKGehZdOWmSH1CM0n3N7FBmIwOmDH0V5BDzSSX5485ooXfs0E7sQO\nsavHZzdCw1zMsI/IBwHNLdi1x9lBNL2KiJQ8Yl2xYIyN0pOxwn5cnCw4tSdX\n0fzR3N8NrLdk7HviaAkktquf+NF4MRax0AIBfOh6qHzwNXvHWuPI6NPnqRZE\nXA37Kx+nuBPad83ZwBL+yXdVDLgxSk0kFd+febJ4WPwoehOBxUkBcvmacmjD\nJxIgPLE/DRyLK3YkyDyPb1PUBwGq4OxHDDiJeDJuPQLm9O6QRSLky9Z9kgND\nUfjE9lArJDac2ddinUdHp2kHfscypcUkQIx8eAFJCYgQRVj+mNXuKckTaiHx\nk9JLo+/UbRsUSU70dl/h1e28qTi1vyPLgfQ1BsJhZrfjFkvHV6DLDKK9KPjT\nMYmydnVfnofnS7VxmMjVeG/3jTO+mC4RkwLPhRD3rJvpF6X5+T5hQdAlvS8p\ngr5wUzGuF1BbfqxZIzrHj6DY/fLKQg5TJZmK3q1w8KRttmUj2WtxeSom/SOZ\ntF+n\r\n=RKGB\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/index.js","gitHead":"8fe0649bd2898b2918584ec52fad70645caef402","scripts":{"flow":"flow","jest":"jest --maxWorkers=2","lint":"eslint src/**","test":"npm run jest -- --coverage --coverageReporters=text-lcov | ./node_modules/coveralls/bin/coveralls.js","build":"babel src -d build --copy-files","jest:watch":"npm run jest -- --watchAll","lint:watch":"esw -w lib/**","prepublish":"npm run build"},"_npmUser":{"name":"kyleatcodesignal","email":"kyle@codesignal.com"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.","repository":{"url":"git+https://github.com/CodeSignal/redux-rags.git","type":"git"},"_npmVersion":"5.6.0","description":"Redux Reducers, Actions, and Getters. Simplified!","directories":{},"_nodeVersion":"8.9.4","_hasShrinkwrap":false,"devDependencies":{"jest":"^23.6.0","redux":"^4.0.1","eslint":"^5.8.0","flow-bin":"^0.86.0","coveralls":"^3.0.2","@babel/cli":"^7.2.3","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","@babel/core":"^7.2.2","babel-eslint":"^10.0.1","eslint-watch":"^4.0.2","babel-polyfill":"^6.26.0","@babel/register":"^7.0.0","@babel/preset-env":"^7.3.1","@babel/preset-flow":"^7.0.0","regenerator-runtime":"^0.13.1","eslint-plugin-import":"^2.14.0","babel-plugin-transform-imports":"^1.5.1","@babel/plugin-transform-modules-commonjs":"^7.2.0","@babel/plugin-proposal-object-rest-spread":"^7.3.1"},"_npmOperationalInternal":{"tmp":"tmp/redux-rags_1.2.0_1548188651034_0.30186354854713393","host":"s3://npm-registry-packages"}},"1.2.1-beta":{"name":"redux-rags","version":"1.2.1-beta","keywords":["redux"],"author":{"name":"CodeSignal"},"license":"MIT","_id":"redux-rags@1.2.1-beta","maintainers":[{"name":"kyleatcodesignal","email":"kyle@codesignal.com"}],"homepage":"https://github.com/CodeSignal/redux-rags#readme","bugs":{"url":"https://github.com/CodeSignal/redux-rags/issues"},"dist":{"shasum":"d63eb858cd10dea8f54a751167df9fdaa68a1d7b","tarball":"https://registry.npmjs.org/redux-rags/-/redux-rags-1.2.1-beta.tgz","fileCount":10,"integrity":"sha512-vo6ZED08aWv2iuJZYd6SAgfYuNkQ9Ag9CP1F0psQT8FDLffYpdGmGXVObkUEsUeubrMnXex/UfVxE2+5qM92iQ==","signatures":[{"sig":"MEUCIDKv6NwqFkcUL0B61si5VmruUCE/yPWz5axAUs6B1M/mAiEAkDr+0ZNTrDyPtQtKBzSa4Hlum9kB8KFcU1TB6UAkD5w=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":43053,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcT0T4CRA9TVsSAnZWagAAULUP/RhFS1BoblU/0t+on3vQ\nCcY3POqInVnZAZ/bXafjx32jQHCd+NlBn41SCCNPHrr2eEeNo2glktOkf6V2\nnHqYIACV4dwd5pkOpTCn6QaKPmWV5wUj7ZmG1TmLIfp20ohthTz7eoSGSbiM\nIC28/ybmq0wKBUaU/Wgdu7JfNJ3vE5L2fMdSpnC9+LJzFruO7xuBKPYzuz13\nLcjRCl2vrQFsZIYCsRegd3cjv92/7GaIjhBOOHAj+sDNo0G9DP8rcvmvIpc0\nBfg/DebWsJnAIHVQU2BuBWDY8OQ0I5/19rnyc5Gp14AT7+htg6eA/FF/PZo2\nFInCUCoMxOu9NA2fJtK3f28yxDV08nhXrEnNkgbJ9zaGY6IDbe3GJiktMyno\nu/0Vo1XavAqZ5llhymn+1OWACaFBNtcAc4HMnO6S4vrqgq/4yZzffh8K/5o+\nj+I+ittZO9TZbbn4+ofxRKdGlan2e+K2jV4ggg3YS07nO4kfWFREck8nOUse\nPF6Rk/GsPUlhMgJawe+g3sbkRZZTuoeLEOFqa4SyFJtuxHShj2acL7dFnoPs\nJZhoLQzSvYQQsZJfyzDLwmaEVI8iI0lHUYQ1ZlVsF97ZB/rSrEr455Z/Qu+4\nj9tRzOGnsIf5L2xGJKp1C+Xn6H+lw6EQcfzpb09FqtYzND+aejRVJqstwQQW\n1XrD\r\n=tuRQ\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/index.js","readme":"# Redux-RAGs\n[![npm version](https://img.shields.io/npm/v/redux-rags.svg?style=flat-square)](https://www.npmjs.com/package/redux-rags)\n[![npm downloads](https://img.shields.io/npm/dm/redux-rags.svg?style=flat-square)](https://www.npmjs.com/package/redux-rags)\n[![Coverage Status](https://coveralls.io/repos/github/CodeSignal/redux-rags/badge.svg?branch=master)](https://coveralls.io/github/CodeSignal/redux-rags?branch=master)\n\nRedux **R**educers, **A**ctions, and **G**etters: Simplified!\n\nTLDR: No need to create or connect subreducers for simple async requests. Your Redux connection is handled by a single function\n```js\nconst rag = ragFactory({ load });\n\nconst newLoad = rag.actions.load;\nconst getData = rag.getters.getData;\nconnect(state => ({ data: getData(store) }), { load: newLoad })(Component);\n```\n\n## Motivation\nWe found that a lot of our reducers were redefining a common theme. We have some endpoint\nthat we want to query for data. We'd run through the same steps every time:\n* Begin loading state\n* Fetch Data\n* End Loading State\n* Set data or error\n\n<center>\n  <img src=\"media/data-flow.png\" width=\"80%\" />\n</center>\n\nAnd that's it: A mini state machine that we redefined over and over, creating dozens of\nsimilar actions and subreducers. But what if there were an easier way? What if we could\ndefine that mini state machine once and re-use the logic with each kind of query? Well `redux-rags` is\nhere to help!\n\n## Injecting a Subreducer?!\nUsing Redux requires quite a lot of boilerplate. You need to create Actions and maybe Action Creators. You need to create Reducers and place them somewhere in the store, maybe as a part of another reducer. There are a myriad of tools out there to help you simplify this process, and we're adding another to the list! This one is built around the idea that there are some pieces of state that you would like to have stored outside of a particular rendering context, but don't want to go through all the migration work to create a custom reducer in the store for this data. You want to take advantage of the benefits of Redux without trudging through all the boilerplate.\n\nSo what if you had a way to add subreducers to Redux? Then you could write code that generates the subreducer structures you use frequently and inject them! You could now start writing subreducers aimed at general cases of data interaction and vastly reduce the time it takes to migrate a piece of state to Redux. We've provided our most useful abstraction here, a simple data request lifecycle. Hopefully you can see the opportunities that injecting a subreducer provides and can use the code here as an example for how to start reusing your own subreducer logic.\n\nFor more details, I encourage you to check out [this Medium article by Jimmy Shen on subreducer injection.](https://medium.com/@jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7)\n\n## Usage\n\n\n### Single File Redux Connection Example\nHere's how you'd interact with Redux for a data request. We're going to create the actions and\nautomatically create and inject the subreducer. The returned `load` function will take the same\narguments as the `load` function passed in, so you have control over the `load` thunk.\n\nHere we want to hit an endpoint with the `userId` parameter. We'll avoid worrying about\nloading states or anything for now. The call to `ragFactory` creates the actions and injects\nthe subreducer for us. Then this state information is stored in Redux, so when users return to the\ncomponent they'll see the cached data. [Play with a similar example on CodeSandbox](https://codesandbox.io/s/k218x138l7)\n\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { ragFactory } from 'redux-rags';\n\n// Pass load to factory, factory returns basically the same load that we can give to connect.\nconst { actions: { load }, getters: { getData } } = ragFactory({\n  load: (userId) => axios.get('/users', { userId })\n});\n\nfunction ViewUser({userId, load, user}) {\n  return (\n    <div>\n      <button onClick={() => load(userId)}>Load User Info</button>\n      {user && <UserData data={user} />}\n    </div>\n  )\n}\n\nexport default connect(state => ({ user: getData(state)}), { load })(ViewUser)\n```\n\n\nAnd that's it! That's all you need to do; no need to manually create or place a subreducer\nanywhere. The subreducer is pretty basic, it holds the return of `load` in the data attribute.\nIt also keeps track of meta information, like last load time, number of times loaded, and errors.\n\nIf you want to keep track of data by parameters passed to `load`, i.e. loading a whole bunch\nof users, you'll want to use `ragFactoryMap`. This will use `ragFactory` behind the scenes to\ncreate the simple mini-reducer for each set of parameters you specify.\n\n\n## Prerequisites\nYou'll need `redux-thunk` and to restructure your `createRootReducer` function. We'll\nneed to handle the addition of dynamic reducers!\n\nHere's what your Redux store creation will look like:\n```js\nimport { combineReducers, createStore, applyMiddleware } from 'redux';\nimport thunk from 'redux-thunk';\n\nimport { combineAsyncReducers, configureRags } from 'redux-rags';\n\n// Import a few reducers like you normally would to create a top level reducer: we'll use `userReducer` as an example\nimport userReducer from './userReducer';\n\nconst createRootReducer = (dynamicReducers: Object = {}) => {\n  dynamicReducers = combineAsyncReducers(combineReducers, dynamicReducers);\n\n  return combineReducers(Object.assign({}, dynamicReducers, {\n    // Then list your reducers below\n    userReducer\n  }));\n}\n\nconst middleware = applyMiddleware(thunk);\nconst store = createStore(createRootReducer(), compose(middleware));\nconfigureRags(store, createRootReducer);\n```\n\n## What Actions and Getters are there?\nGetters take in the full store as the argument. For `ragFactory`, you'll have access to the following getters:\n\n- `get`: Returns `BoilerState<T>`, so an object that looks like `{ data, meta }`.\n- `getData`: Returns the `data` attribute from `BoilerState<T>`. It would be type `?T`, whatever your load / update functions passed to the `ragFactory` return.\n- `getMeta`: Returns the `meta` attribute from `BoilerState<T>`. This will be an object that looks like `{ loading, lastLoadTime, errors }`, with a few more properties. Look for the types / additional properties in the type definition.\n- `getIsLoading`: Returns the `loading` attribute from the `meta` field. A common use case is for quick access to the loading state, so a special getter is provided for this meta attribute.\n\nActions are implementations of commonly used features to manipulate the state. Most of these are action creators, with `load` and `update` being thunks.\n\n- `beginLoading`: Sets `loading` meta property to true.\n- `endLoading`: Sets `loading` meta property to false.\n- `reset`: Returns the subreducer to the initial state.\n- `errors`: Sets the errors meta value.\n- `clearErrors`: Clears the errors meta value.\n- `updateData`: Sets the data value for the subreducer.\n- `update`: Thunk. Calls the `update` function passed in to `ragsFactory` and sets the data value to the result. Might also set the error attribute if the `update` function throws an error. Passed in function should look like: `(dataValue, ...params) => newData`. Returned func has signature: `(...params) => newData`. This is because the current data value will be added to the function internally.\n- `load`: Thunk. Calls the appropriate sequence of `beginLoading`, `updateData`, `endLoading` actions while calling the `load` function passed to `ragsFactory`. If there are errors while executing, the error meta value will be set. Passed in function looks like: `(...params) => newData`, returned function looks like `(...params) => newData`.\n\n## Details and explanations of exports\n\n### A few types to consider:\nThe generated subreducer uses the following type for the state:\n```js\ntype BoilerState<T> = {\n  data: ?T,\n  meta: {\n    loading: boolean,\n    loaded: boolean,\n    changeCount: number,\n    lastChangeTime: ?number,\n    errors?: *\n  }\n}\n```\n|  Meta Property | Description |\n|:----:|:---:|\n| loading | Is the data currently being loaded |\n| loaded | Has the data every been loaded? Equivalent to `changeCount > 0` |\n| changeCount | Number of times the data has been loaded / updated |\n| lastChangeTime | The time of the last change |\n| errors | Error object from the `load` or `update` function, if any |\n\n### configureRags\nSignature: `(store: Object, createRootReducer: Function) => void`.\n\nThis function is how you set up the module, giving it access to your Redux store. This is used to configure the reducer\ninjector. We need `store` for the [`replaceReducer`](https://redux.js.org/api/store#replaceReducer) method. And we'll need to have a formatted `createRootReducer` function that accepts dynamic reducers. We've provided a `combineAsyncReducers` function to make this easier.\n\n### combineAsyncReducers\nSignature: `(combineReducers: Function, dynamicReducers: Object) => newDynamicReducers`\n\nThis function is designed to recursively call the `combineReducers` function on the second parameter `dynamicReducers`. This means that whatever the structure of `dynamicReducers` we can register the functions with `redux`. The package internally will add values underneath `@@redux-rags` and `@@redux-rags/map` keys in the `dynamicReducers` object. \n\n### injectReducer\nSignature: `(keys: Array<string>, reducer: Function) => void`\n\nThis function will insert the given `reducer` at the nested key location in `dynamicReducers` specified by `keys`. For example, if the user has called `ragFactory` with the name `'user info'` internally we will call this function with `injectReducer(['@@redux-rags', 'user info'], subreducer)`. This will set values so that `dynamicReducers['@@redux-rags']['user info'] == subreducer` and update the store with the newly injected subreducer.\n\nYou can call this function and inject your own subreducers. You could just use `configureRags`, `combineAsyncReducers`, and `injectReducer` to setup your store to allow for dynamic reducers and skip the rest of this module. \n\n### ragFactory\n\nThough both `load` and `update` are optional, you pass at least one. With neither of these, there is no good way to\nupdate the data or metadata fields.\n\nThe `ragFactory` will place the dynamically added subreducers in the `@@redux-rags` top leve of the Redux store.\nIf you want to place this somewhere else, just put the returned `subreducer` somewhere in your Redux store and\npass in the `getInStore` function.\n\n|  Props  |  Type  |  Optional  |  Description  |\n|:-------:|:------:|:----------:|:-------------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to Redux and the automatically injected subreducer will use that name. |\n| load | Function | yes | A function that loads data |\n| getInitialState | Function | yes | Function to create initial data for the return of `load`, will use null if not defined |\n| getInStore | Function | yes | `(state) => state.some.path.to.subreducer` Function to locate the subreducer in the store. Will place in default location if not specified, otherwise will use this location in getters. |\n| update | Function | yes | (dataValue, ...args) => { return newData; } A function to manipulate the data attribute. Returned `update` will have signature `(...args) => void` |\n| loadOnlyOnce | boolean | yes | Prevent the `load` function from being called more than once. |\n| partialReducer | Function | yes | Allows you to hook in to other actions in the Redux store and modify the state. You'll be working with the BoilerState argument. Extend the reducer and listen to other actions, for example you can could clear the data on user logout. Write this like a reducer to extend the functionality of the generated boilerplate. You can also delay assignment if you want to utilize actions in the returned Actions object, just assign the function to `subreducer.partialReducer`.|\n\n### ragFactoryMap\nWhat's this `ragFactoryMap`? Well if you want to cache data based on the query parameters, then the `ragFactoryMap` is for you!\nFor each collection of args, a new reducer will be injected. The params are stringified and used as the key for the injected subreducer.\n\nThe `ragFactoryMap` builds on top of `ragFactory`. That factory is used internally to generate the subreducers and actions for each key.\n\n| Props | Type | Optional | Description |\n|:-----:|:----:|:--------:|:-----------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to Redux and the automatically injected subreducer will use that name. |\n| load | Function | no | A function that loads data. The returned function will have the same signature. |\n| getInitialState | Function | yes | Function to create initial data for the return of load, will use null if not defined |\n\n\n## Additional Examples\n\n### Defining the Redux interactions in a separate file and importing\nWe'll use a basic fetch for this example, hitting some json endpoint. The returned load function\nhas the same signature as the function we passed in, so in this case `loadFaq` will not use\nany parameters.\n```js\nimport { ragFactory } from 'redux-rags';\n\nconst axiosLoadFaq = () => fetch('http://api.example.com/data.json');\nconst { actions, getters } = ragFactory({ load: axiosLoadFaq });\n\nexport {\n  loadFaq: actions.load,\n  getFaq: getters.getData,\n  getIsFaqLoading: getters.getIsLoading,\n};\n```\n\n#### Use that Data Request in a React Component\nImport the Getters and Action as you normally would and use them with `react-redux`. If you're used to\ndefining thunks and getters in a different file and import them, then the code below should look\nremarkably similar to code that you've already seen or even written.\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { loadFaq, getFaq, getIsFaqLoading } from './faqData';\nimport Loading from './Loading';\nimport FaqItem from './FaqItem';\n\nclass Faq extends React.Component {\n  componentDidMount() {\n    this.props.loadFaq();\n  }\n\n  render() {\n    const { isLoading, faqData } = this.props;\n    if (isLoading) {\n      return <Loading />;\n    }\n\n    return (\n      <React.Fragment>\n        {faqData.map((data, index) => <FaqItem key={index} data={data} />}\n      </React.Fragment>\n    )\n  }\n}\n\nconst mapStateFromProps = state => ({\n  faqData: getFaq(state),\n  isLoading: getIsFaqLoading(state)\n});\n\nexport default connect(mapStateFromProps, { loadFaq })(Faq);\n```\n\n### Placing the subreducer in a custom location in Redux\nConnect subreducer to your desired location in the Redux store, and tell the generator\nwhere you put it. Getters are also factories for you. If you don't care where the\nsubreducer lives, don't pass in a `getInStore` method and the generator will place it for you.\nIn this case taking `subreducer` and adding it to the `combineSubreducers` function in the `currentUser` subreducer.\nBut you can place it wherever you want, just update the `getInStore` function passed to `ragFactory`.\n```js\nimport { generator };\nconst { actions, subreducer, getters } = generator({\n  name: 'MY_DATA',\n  load: (param) => methodService.run('fetchSomething', param),\n  getInStore: (store) => store && store.currentUser && store.currentUser.my_data\n});\n```\n\n### Using update to manipulate the stored value\nIn addition to `load` you can also pass in an `update` function, which will take the\ncurrent `data` value as the first parameter. An example of this might be an update\nfunction that increments the current value by 2. The signature of the returned function\nis slightly different, as the `data` first argument is passed internally, so you pass\nin a function that takes parameters `(currentData, ...additionalParams)` and the returned\n`actions.update` function will take the parameters `(...additionalParams)`.\n[View an example that relies entirely on the `update` funciton on CodeSandbox](https://codesandbox.io/s/1307vwj8zj) \n```js\nimport { generator };\nconst { actions : { update }, getters } = generator({\n  name: 'MY_DATA',\n  update: (currentData) => currentData + 2,\n});\n```\n\n### Using the ragFactoryMap\nNot much to say, it's pretty much how you would use `ragFactory`, except treating the input arguments\nas a key for a different mini state machine.\n\n```js\n const { action: { load }, getters: { getData, getMeta } } =\n   ragFactoryMap({ load: Function });\n\n // In a component after `load` was passed in through mapDispatchToProps.\n\nclass Component extends React.Component {\n  componentDidMount() {\n    const { loaded, load, taskId, userId } = this.props;\n    // Load the data for given userId and taskId if not loaded\n    !loaded && load(userId, taskId);\n  }\n  // ... Other functions of the class\n}\n\nfunction mapStateToProps(state, props) {\n  const { userId, taskId } = props;\n  return {\n    loaded: getMeta(userId, taskId).loaded,\n    taskData: getData(userId, taskId)\n  }\n}\nexport default connect(mapStateToProps)(Component);\n```\n\n## License\nMIT\n\n## Thanks\n\nHuge thanks to Jimmy Shen for [the amazing Medium article already linked above](https://medium.com/@jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7)\non dynamically injecting reducers into the Redux store.\n","gitHead":"b30af537a9a9081a961d0855e358f1137d26c0db","scripts":{"flow":"flow","jest":"jest --maxWorkers=2","lint":"eslint src/**","test":"npm run jest -- --coverage --coverageReporters=text-lcov | ./node_modules/coveralls/bin/coveralls.js","build":"babel src -d build --copy-files","jest:watch":"npm run jest -- --watchAll","lint:watch":"esw -w lib/**","prepublish":"npm run build"},"_npmUser":{"name":"kyleatcodesignal","email":"kyle@codesignal.com"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.","repository":{"url":"git+https://github.com/CodeSignal/redux-rags.git","type":"git"},"_npmVersion":"5.6.0","description":"Redux Reducers, Actions, and Getters. Simplified!","directories":{},"_nodeVersion":"8.9.4","_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"jest":"^23.6.0","redux":"^4.0.1","eslint":"^5.8.0","flow-bin":"^0.86.0","coveralls":"^3.0.2","@babel/cli":"^7.2.3","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","@babel/core":"^7.2.2","babel-eslint":"^10.0.1","eslint-watch":"^4.0.2","babel-polyfill":"^6.26.0","@babel/register":"^7.0.0","@babel/preset-env":"^7.3.1","@babel/preset-flow":"^7.0.0","regenerator-runtime":"^0.13.1","eslint-plugin-import":"^2.14.0","babel-plugin-transform-imports":"^1.5.1","@babel/plugin-proposal-class-properties":"^7.3.0","@babel/plugin-transform-modules-commonjs":"^7.2.0","@babel/plugin-proposal-object-rest-spread":"^7.3.1"},"_npmOperationalInternal":{"tmp":"tmp/redux-rags_1.2.1-beta_1548698871926_0.3976637019931768","host":"s3://npm-registry-packages"}},"1.2.1-beta2":{"name":"redux-rags","version":"1.2.1-beta2","keywords":["redux"],"author":{"name":"CodeSignal"},"license":"MIT","_id":"redux-rags@1.2.1-beta2","maintainers":[{"name":"kyleatcodesignal","email":"kyle@codesignal.com"}],"homepage":"https://github.com/CodeSignal/redux-rags#readme","bugs":{"url":"https://github.com/CodeSignal/redux-rags/issues"},"dist":{"shasum":"94af6a410401ad19a3e3814556681ec59540114e","tarball":"https://registry.npmjs.org/redux-rags/-/redux-rags-1.2.1-beta2.tgz","fileCount":10,"integrity":"sha512-cMboD03/Sw1g0HnaH7wGYhjRcdT8DyLNkjeHT35w52/SpWQ83/1bCJXzGy5hD1Fw0vEa9P2+tnTo65o0SP2nQw==","signatures":[{"sig":"MEUCIQCx1XsrcJekXC1LTzdk/JC7PsYpl4uiYFdgYu/OdsjxGQIgCtOgNco/6GZ6tii28EukD5P2FqMskBHyjG9bBIBSVpE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":42558,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcT0abCRA9TVsSAnZWagAAyXIP/1qLsIbY5/fPSRSaV4ay\n/d9lNBhgkXV1YW9LdV0eBwhzYFXg5ScADNTtzN7Czcq6ab2NRQl4wFd+1Tm7\n3D0motljJeVMftydJR0QN+A57XjsMzuB+X7wNmcyE1JABO07x1NGQfQJ7gWC\n7MeBzxfYfuVqluRbQdKH7KvvYB+L8RXVJuVMJEgqkhiA9T9OFhA5ku6pGCnD\nLJ20N1blwad1V7h2o6bAcerx870UGBOVcAaM+yvJnNVMjF2PSOImA5yfiFGF\nRRsxl2oLx1evw6TTCYn045GWgqPmkY2Avzya97YYpHhB4YzRRLH7bDxZyRsu\nXhAOA4gRDOye3CI//QazgS0jeAZV8H4MHrL2FJVpvL95a1Yu+Yj20AxEvkMg\nQAGGbGSwY4QHdjhZ3Ah0fpl2s/0PP//Dw34N3s4euLfsBgAVYruMeSH2+bfl\ncF1eZrAvn2iIXqxBYTc3zWot4Fqo316kbT37qedNH0TNw0yCcqIh2rWI0hbe\nGE3ubHLUBNNDG5XA3idpbiKHCvA8tqHziMnrmhR5CpXo8dQHMq8FnQgEVTSq\nwNc13mgKkIZofEEK9p+x3nBhaVtkl3I0+E4qZtd4iW8Ipn6hfEEEW3QIOKcq\n+mz5SJiYrOO3wISz4Jitpvvni1IQRYPGS8FSPJJovSZyBL2T9XOF2aL31YQS\nr1TD\r\n=vVtx\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/index.js","readme":"# Redux-RAGs\n[![npm version](https://img.shields.io/npm/v/redux-rags.svg?style=flat-square)](https://www.npmjs.com/package/redux-rags)\n[![npm downloads](https://img.shields.io/npm/dm/redux-rags.svg?style=flat-square)](https://www.npmjs.com/package/redux-rags)\n[![Coverage Status](https://coveralls.io/repos/github/CodeSignal/redux-rags/badge.svg?branch=master)](https://coveralls.io/github/CodeSignal/redux-rags?branch=master)\n\nRedux **R**educers, **A**ctions, and **G**etters: Simplified!\n\nTLDR: No need to create or connect subreducers for simple async requests. Your Redux connection is handled by a single function\n```js\nconst rag = ragFactory({ load });\n\nconst newLoad = rag.actions.load;\nconst getData = rag.getters.getData;\nconnect(state => ({ data: getData(store) }), { load: newLoad })(Component);\n```\n\n## Motivation\nWe found that a lot of our reducers were redefining a common theme. We have some endpoint\nthat we want to query for data. We'd run through the same steps every time:\n* Begin loading state\n* Fetch Data\n* End Loading State\n* Set data or error\n\n<center>\n  <img src=\"media/data-flow.png\" width=\"80%\" />\n</center>\n\nAnd that's it: A mini state machine that we redefined over and over, creating dozens of\nsimilar actions and subreducers. But what if there were an easier way? What if we could\ndefine that mini state machine once and re-use the logic with each kind of query? Well `redux-rags` is\nhere to help!\n\n## Injecting a Subreducer?!\nUsing Redux requires quite a lot of boilerplate. You need to create Actions and maybe Action Creators. You need to create Reducers and place them somewhere in the store, maybe as a part of another reducer. There are a myriad of tools out there to help you simplify this process, and we're adding another to the list! This one is built around the idea that there are some pieces of state that you would like to have stored outside of a particular rendering context, but don't want to go through all the migration work to create a custom reducer in the store for this data. You want to take advantage of the benefits of Redux without trudging through all the boilerplate.\n\nSo what if you had a way to add subreducers to Redux? Then you could write code that generates the subreducer structures you use frequently and inject them! You could now start writing subreducers aimed at general cases of data interaction and vastly reduce the time it takes to migrate a piece of state to Redux. We've provided our most useful abstraction here, a simple data request lifecycle. Hopefully you can see the opportunities that injecting a subreducer provides and can use the code here as an example for how to start reusing your own subreducer logic.\n\nFor more details, I encourage you to check out [this Medium article by Jimmy Shen on subreducer injection.](https://medium.com/@jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7)\n\n## Usage\n\n\n### Single File Redux Connection Example\nHere's how you'd interact with Redux for a data request. We're going to create the actions and\nautomatically create and inject the subreducer. The returned `load` function will take the same\narguments as the `load` function passed in, so you have control over the `load` thunk.\n\nHere we want to hit an endpoint with the `userId` parameter. We'll avoid worrying about\nloading states or anything for now. The call to `ragFactory` creates the actions and injects\nthe subreducer for us. Then this state information is stored in Redux, so when users return to the\ncomponent they'll see the cached data. [Play with a similar example on CodeSandbox](https://codesandbox.io/s/k218x138l7)\n\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { ragFactory } from 'redux-rags';\n\n// Pass load to factory, factory returns basically the same load that we can give to connect.\nconst { actions: { load }, getters: { getData } } = ragFactory({\n  load: (userId) => axios.get('/users', { userId })\n});\n\nfunction ViewUser({userId, load, user}) {\n  return (\n    <div>\n      <button onClick={() => load(userId)}>Load User Info</button>\n      {user && <UserData data={user} />}\n    </div>\n  )\n}\n\nexport default connect(state => ({ user: getData(state)}), { load })(ViewUser)\n```\n\n\nAnd that's it! That's all you need to do; no need to manually create or place a subreducer\nanywhere. The subreducer is pretty basic, it holds the return of `load` in the data attribute.\nIt also keeps track of meta information, like last load time, number of times loaded, and errors.\n\nIf you want to keep track of data by parameters passed to `load`, i.e. loading a whole bunch\nof users, you'll want to use `ragFactoryMap`. This will use `ragFactory` behind the scenes to\ncreate the simple mini-reducer for each set of parameters you specify.\n\n\n## Prerequisites\nYou'll need `redux-thunk` and to restructure your `createRootReducer` function. We'll\nneed to handle the addition of dynamic reducers!\n\nHere's what your Redux store creation will look like:\n```js\nimport { combineReducers, createStore, applyMiddleware } from 'redux';\nimport thunk from 'redux-thunk';\n\nimport { combineAsyncReducers, configureRags } from 'redux-rags';\n\n// Import a few reducers like you normally would to create a top level reducer: we'll use `userReducer` as an example\nimport userReducer from './userReducer';\n\nconst createRootReducer = (dynamicReducers: Object = {}) => {\n  dynamicReducers = combineAsyncReducers(combineReducers, dynamicReducers);\n\n  return combineReducers(Object.assign({}, dynamicReducers, {\n    // Then list your reducers below\n    userReducer\n  }));\n}\n\nconst middleware = applyMiddleware(thunk);\nconst store = createStore(createRootReducer(), compose(middleware));\nconfigureRags(store, createRootReducer);\n```\n\n## What Actions and Getters are there?\nGetters take in the full store as the argument. For `ragFactory`, you'll have access to the following getters:\n\n- `get`: Returns `BoilerState<T>`, so an object that looks like `{ data, meta }`.\n- `getData`: Returns the `data` attribute from `BoilerState<T>`. It would be type `?T`, whatever your load / update functions passed to the `ragFactory` return.\n- `getMeta`: Returns the `meta` attribute from `BoilerState<T>`. This will be an object that looks like `{ loading, lastLoadTime, errors }`, with a few more properties. Look for the types / additional properties in the type definition.\n- `getIsLoading`: Returns the `loading` attribute from the `meta` field. A common use case is for quick access to the loading state, so a special getter is provided for this meta attribute.\n\nActions are implementations of commonly used features to manipulate the state. Most of these are action creators, with `load` and `update` being thunks.\n\n- `beginLoading`: Sets `loading` meta property to true.\n- `endLoading`: Sets `loading` meta property to false.\n- `reset`: Returns the subreducer to the initial state.\n- `errors`: Sets the errors meta value.\n- `clearErrors`: Clears the errors meta value.\n- `updateData`: Sets the data value for the subreducer.\n- `update`: Thunk. Calls the `update` function passed in to `ragsFactory` and sets the data value to the result. Might also set the error attribute if the `update` function throws an error. Passed in function should look like: `(dataValue, ...params) => newData`. Returned func has signature: `(...params) => newData`. This is because the current data value will be added to the function internally.\n- `load`: Thunk. Calls the appropriate sequence of `beginLoading`, `updateData`, `endLoading` actions while calling the `load` function passed to `ragsFactory`. If there are errors while executing, the error meta value will be set. Passed in function looks like: `(...params) => newData`, returned function looks like `(...params) => newData`.\n\n## Details and explanations of exports\n\n### A few types to consider:\nThe generated subreducer uses the following type for the state:\n```js\ntype BoilerState<T> = {\n  data: ?T,\n  meta: {\n    loading: boolean,\n    loaded: boolean,\n    changeCount: number,\n    lastChangeTime: ?number,\n    errors?: *\n  }\n}\n```\n|  Meta Property | Description |\n|:----:|:---:|\n| loading | Is the data currently being loaded |\n| loaded | Has the data every been loaded? Equivalent to `changeCount > 0` |\n| changeCount | Number of times the data has been loaded / updated |\n| lastChangeTime | The time of the last change |\n| errors | Error object from the `load` or `update` function, if any |\n\n### configureRags\nSignature: `(store: Object, createRootReducer: Function) => void`.\n\nThis function is how you set up the module, giving it access to your Redux store. This is used to configure the reducer\ninjector. We need `store` for the [`replaceReducer`](https://redux.js.org/api/store#replaceReducer) method. And we'll need to have a formatted `createRootReducer` function that accepts dynamic reducers. We've provided a `combineAsyncReducers` function to make this easier.\n\n### combineAsyncReducers\nSignature: `(combineReducers: Function, dynamicReducers: Object) => newDynamicReducers`\n\nThis function is designed to recursively call the `combineReducers` function on the second parameter `dynamicReducers`. This means that whatever the structure of `dynamicReducers` we can register the functions with `redux`. The package internally will add values underneath `@@redux-rags` and `@@redux-rags/map` keys in the `dynamicReducers` object. \n\n### injectReducer\nSignature: `(keys: Array<string>, reducer: Function) => void`\n\nThis function will insert the given `reducer` at the nested key location in `dynamicReducers` specified by `keys`. For example, if the user has called `ragFactory` with the name `'user info'` internally we will call this function with `injectReducer(['@@redux-rags', 'user info'], subreducer)`. This will set values so that `dynamicReducers['@@redux-rags']['user info'] == subreducer` and update the store with the newly injected subreducer.\n\nYou can call this function and inject your own subreducers. You could just use `configureRags`, `combineAsyncReducers`, and `injectReducer` to setup your store to allow for dynamic reducers and skip the rest of this module. \n\n### ragFactory\n\nThough both `load` and `update` are optional, you pass at least one. With neither of these, there is no good way to\nupdate the data or metadata fields.\n\nThe `ragFactory` will place the dynamically added subreducers in the `@@redux-rags` top leve of the Redux store.\nIf you want to place this somewhere else, just put the returned `subreducer` somewhere in your Redux store and\npass in the `getInStore` function.\n\n|  Props  |  Type  |  Optional  |  Description  |\n|:-------:|:------:|:----------:|:-------------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to Redux and the automatically injected subreducer will use that name. |\n| load | Function | yes | A function that loads data |\n| getInitialState | Function | yes | Function to create initial data for the return of `load`, will use null if not defined |\n| getInStore | Function | yes | `(state) => state.some.path.to.subreducer` Function to locate the subreducer in the store. Will place in default location if not specified, otherwise will use this location in getters. |\n| update | Function | yes | (dataValue, ...args) => { return newData; } A function to manipulate the data attribute. Returned `update` will have signature `(...args) => void` |\n| loadOnlyOnce | boolean | yes | Prevent the `load` function from being called more than once. |\n| partialReducer | Function | yes | Allows you to hook in to other actions in the Redux store and modify the state. You'll be working with the BoilerState argument. Extend the reducer and listen to other actions, for example you can could clear the data on user logout. Write this like a reducer to extend the functionality of the generated boilerplate. You can also delay assignment if you want to utilize actions in the returned Actions object, just assign the function to `subreducer.partialReducer`.|\n\n### ragFactoryMap\nWhat's this `ragFactoryMap`? Well if you want to cache data based on the query parameters, then the `ragFactoryMap` is for you!\nFor each collection of args, a new reducer will be injected. The params are stringified and used as the key for the injected subreducer.\n\nThe `ragFactoryMap` builds on top of `ragFactory`. That factory is used internally to generate the subreducers and actions for each key.\n\n| Props | Type | Optional | Description |\n|:-----:|:----:|:--------:|:-----------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to Redux and the automatically injected subreducer will use that name. |\n| load | Function | no | A function that loads data. The returned function will have the same signature. |\n| getInitialState | Function | yes | Function to create initial data for the return of load, will use null if not defined |\n\n\n## Additional Examples\n\n### Defining the Redux interactions in a separate file and importing\nWe'll use a basic fetch for this example, hitting some json endpoint. The returned load function\nhas the same signature as the function we passed in, so in this case `loadFaq` will not use\nany parameters.\n```js\nimport { ragFactory } from 'redux-rags';\n\nconst axiosLoadFaq = () => fetch('http://api.example.com/data.json');\nconst { actions, getters } = ragFactory({ load: axiosLoadFaq });\n\nexport {\n  loadFaq: actions.load,\n  getFaq: getters.getData,\n  getIsFaqLoading: getters.getIsLoading,\n};\n```\n\n#### Use that Data Request in a React Component\nImport the Getters and Action as you normally would and use them with `react-redux`. If you're used to\ndefining thunks and getters in a different file and import them, then the code below should look\nremarkably similar to code that you've already seen or even written.\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { loadFaq, getFaq, getIsFaqLoading } from './faqData';\nimport Loading from './Loading';\nimport FaqItem from './FaqItem';\n\nclass Faq extends React.Component {\n  componentDidMount() {\n    this.props.loadFaq();\n  }\n\n  render() {\n    const { isLoading, faqData } = this.props;\n    if (isLoading) {\n      return <Loading />;\n    }\n\n    return (\n      <React.Fragment>\n        {faqData.map((data, index) => <FaqItem key={index} data={data} />}\n      </React.Fragment>\n    )\n  }\n}\n\nconst mapStateFromProps = state => ({\n  faqData: getFaq(state),\n  isLoading: getIsFaqLoading(state)\n});\n\nexport default connect(mapStateFromProps, { loadFaq })(Faq);\n```\n\n### Placing the subreducer in a custom location in Redux\nConnect subreducer to your desired location in the Redux store, and tell the generator\nwhere you put it. Getters are also factories for you. If you don't care where the\nsubreducer lives, don't pass in a `getInStore` method and the generator will place it for you.\nIn this case taking `subreducer` and adding it to the `combineSubreducers` function in the `currentUser` subreducer.\nBut you can place it wherever you want, just update the `getInStore` function passed to `ragFactory`.\n```js\nimport { generator };\nconst { actions, subreducer, getters } = generator({\n  name: 'MY_DATA',\n  load: (param) => methodService.run('fetchSomething', param),\n  getInStore: (store) => store && store.currentUser && store.currentUser.my_data\n});\n```\n\n### Using update to manipulate the stored value\nIn addition to `load` you can also pass in an `update` function, which will take the\ncurrent `data` value as the first parameter. An example of this might be an update\nfunction that increments the current value by 2. The signature of the returned function\nis slightly different, as the `data` first argument is passed internally, so you pass\nin a function that takes parameters `(currentData, ...additionalParams)` and the returned\n`actions.update` function will take the parameters `(...additionalParams)`.\n[View an example that relies entirely on the `update` funciton on CodeSandbox](https://codesandbox.io/s/1307vwj8zj) \n```js\nimport { generator };\nconst { actions : { update }, getters } = generator({\n  name: 'MY_DATA',\n  update: (currentData) => currentData + 2,\n});\n```\n\n### Using the ragFactoryMap\nNot much to say, it's pretty much how you would use `ragFactory`, except treating the input arguments\nas a key for a different mini state machine.\n\n```js\n const { action: { load }, getters: { getData, getMeta } } =\n   ragFactoryMap({ load: Function });\n\n // In a component after `load` was passed in through mapDispatchToProps.\n\nclass Component extends React.Component {\n  componentDidMount() {\n    const { loaded, load, taskId, userId } = this.props;\n    // Load the data for given userId and taskId if not loaded\n    !loaded && load(userId, taskId);\n  }\n  // ... Other functions of the class\n}\n\nfunction mapStateToProps(state, props) {\n  const { userId, taskId } = props;\n  return {\n    loaded: getMeta(userId, taskId).loaded,\n    taskData: getData(userId, taskId)\n  }\n}\nexport default connect(mapStateToProps)(Component);\n```\n\n## License\nMIT\n\n## Thanks\n\nHuge thanks to Jimmy Shen for [the amazing Medium article already linked above](https://medium.com/@jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7)\non dynamically injecting reducers into the Redux store.\n","gitHead":"066ba0694c34530e616fc069a7d98dea5c38322a","scripts":{"flow":"flow","jest":"jest --maxWorkers=2","lint":"eslint src/**","test":"npm run jest -- --coverage --coverageReporters=text-lcov | ./node_modules/coveralls/bin/coveralls.js","build":"babel src -d build --copy-files","jest:watch":"npm run jest -- --watchAll","lint:watch":"esw -w lib/**","prepublish":"npm run build"},"_npmUser":{"name":"kyleatcodesignal","email":"kyle@codesignal.com"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.","repository":{"url":"git+https://github.com/CodeSignal/redux-rags.git","type":"git"},"_npmVersion":"5.6.0","description":"Redux Reducers, Actions, and Getters. Simplified!","directories":{},"_nodeVersion":"8.9.4","_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"jest":"^23.6.0","redux":"^4.0.1","eslint":"^5.8.0","flow-bin":"^0.86.0","coveralls":"^3.0.2","@babel/cli":"^7.2.3","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","@babel/core":"^7.2.2","babel-eslint":"^10.0.1","eslint-watch":"^4.0.2","babel-polyfill":"^6.26.0","@babel/register":"^7.0.0","@babel/preset-env":"^7.3.1","@babel/preset-flow":"^7.0.0","regenerator-runtime":"^0.13.1","eslint-plugin-import":"^2.14.0","babel-plugin-transform-imports":"^1.5.1","@babel/plugin-proposal-class-properties":"^7.3.0","@babel/plugin-transform-modules-commonjs":"^7.2.0","@babel/plugin-proposal-object-rest-spread":"^7.3.1"},"_npmOperationalInternal":{"tmp":"tmp/redux-rags_1.2.1-beta2_1548699291179_0.6967823609621153","host":"s3://npm-registry-packages"}},"1.2.1-beta3":{"name":"redux-rags","version":"1.2.1-beta3","keywords":["redux"],"author":{"name":"CodeSignal"},"license":"MIT","_id":"redux-rags@1.2.1-beta3","maintainers":[{"name":"kyleatcodesignal","email":"kyle@codesignal.com"}],"homepage":"https://github.com/CodeSignal/redux-rags#readme","bugs":{"url":"https://github.com/CodeSignal/redux-rags/issues"},"dist":{"shasum":"cdba8bc6af82c806e3ea2ec6cfbe708188b84800","tarball":"https://registry.npmjs.org/redux-rags/-/redux-rags-1.2.1-beta3.tgz","fileCount":10,"integrity":"sha512-44nrq9Ro6l2tt6CFPAgOwTmqqzA1MotF7ndQO37CFp9HnDtj/vFM4zSLuYXyatghdq0U63zLXeOPGytcKhF1lA==","signatures":[{"sig":"MEUCIQCRfx8NVxWGyU/LewbKzG1j79kmmJL9rganS/7OXx0mkwIgHaLLp+W6MEgFPk2tri91FpabByijuq1kheP/O1DHia8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":42352,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcT6pICRA9TVsSAnZWagAAdAYQAKIQisXnT+Q+FJ6JxkgF\naj9NqbXChguxg7hMN5dShuhavka5WKsUCR0uwwgC9CPeHHwZlTdrYujVtb9a\naSpkDf8q1q9bIp940osIsntw8e+Ok9BnZZcL7w62cMe1jLLRJDlL7dM/o8aa\nVSyJyPunE4dwHZd+VhmPGdMQbl+jeFbcBoQ3ZO1MB0BhQmAauOW6FcQqG2jB\nX7pEdxGZyt7OkKZS9ehz5tyArd5qkA4k0UWDBs/kLF7yHVmNodQ9IE0M0xXC\ncxU/19Ko8+Wna2k5zucCVqdbZx8mc2aWlDEwR1tgEvMxNvtM9JeeC8glL3Ao\nwhafy1A4e5liiv5cpqnVQG6O2QfQAn0S2RSASO8JzeryHkj4EoO7qMVTIWJC\nKqFumM5kZ5sVSUhgVXWU+Wi9DokBeJPgjXrJQ/uULpP5p5W2xM3MOz+HDN8s\noGe+KugDLNy+mqeVU+MiFDpP4hhl4u15xyPOFpjEyejG+jATT9fIHNyeShDH\n1p+gPFqXQGzoVOu7gQvpDs+afpvzArsdgqgSOYJASBARF3B6YQb29ykQxFlR\nF2h2Du+hxpWW1uyuk7KSkJ2Rsr0byDzBtF9+r6xMxEPksbOr7LXJK2sQFk/u\nKdGQZVrfQjlhI0Lf+K0DZYI5lCuIjuSpJVhmperlu+vN70AON7ed0uOa7u4N\nFeye\r\n=j289\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/index.js","readme":"# Redux-RAGs\n[![npm version](https://img.shields.io/npm/v/redux-rags.svg?style=flat-square)](https://www.npmjs.com/package/redux-rags)\n[![npm downloads](https://img.shields.io/npm/dm/redux-rags.svg?style=flat-square)](https://www.npmjs.com/package/redux-rags)\n[![Coverage Status](https://coveralls.io/repos/github/CodeSignal/redux-rags/badge.svg?branch=master)](https://coveralls.io/github/CodeSignal/redux-rags?branch=master)\n\nRedux **R**educers, **A**ctions, and **G**etters: Simplified!\n\nTLDR: No need to create or connect subreducers for simple async requests. Your Redux connection is handled by a single function\n```js\nconst rag = ragFactory({ load });\n\nconst newLoad = rag.actions.load;\nconst getData = rag.getters.getData;\nconnect(state => ({ data: getData(store) }), { load: newLoad })(Component);\n```\n\n## Motivation\nWe found that a lot of our reducers were redefining a common theme. We have some endpoint\nthat we want to query for data. We'd run through the same steps every time:\n* Begin loading state\n* Fetch Data\n* End Loading State\n* Set data or error\n\n<center>\n  <img src=\"media/data-flow.png\" width=\"80%\" />\n</center>\n\nAnd that's it: A mini state machine that we redefined over and over, creating dozens of\nsimilar actions and subreducers. But what if there were an easier way? What if we could\ndefine that mini state machine once and re-use the logic with each kind of query? Well `redux-rags` is\nhere to help!\n\n## Injecting a Subreducer?!\nUsing Redux requires quite a lot of boilerplate. You need to create Actions and maybe Action Creators. You need to create Reducers and place them somewhere in the store, maybe as a part of another reducer. There are a myriad of tools out there to help you simplify this process, and we're adding another to the list! This one is built around the idea that there are some pieces of state that you would like to have stored outside of a particular rendering context, but don't want to go through all the migration work to create a custom reducer in the store for this data. You want to take advantage of the benefits of Redux without trudging through all the boilerplate.\n\nSo what if you had a way to add subreducers to Redux? Then you could write code that generates the subreducer structures you use frequently and inject them! You could now start writing subreducers aimed at general cases of data interaction and vastly reduce the time it takes to migrate a piece of state to Redux. We've provided our most useful abstraction here, a simple data request lifecycle. Hopefully you can see the opportunities that injecting a subreducer provides and can use the code here as an example for how to start reusing your own subreducer logic.\n\nFor more details, I encourage you to check out [this Medium article by Jimmy Shen on subreducer injection.](https://medium.com/@jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7)\n\n## Usage\n\n\n### Single File Redux Connection Example\nHere's how you'd interact with Redux for a data request. We're going to create the actions and\nautomatically create and inject the subreducer. The returned `load` function will take the same\narguments as the `load` function passed in, so you have control over the `load` thunk.\n\nHere we want to hit an endpoint with the `userId` parameter. We'll avoid worrying about\nloading states or anything for now. The call to `ragFactory` creates the actions and injects\nthe subreducer for us. Then this state information is stored in Redux, so when users return to the\ncomponent they'll see the cached data. [Play with a similar example on CodeSandbox](https://codesandbox.io/s/k218x138l7)\n\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { ragFactory } from 'redux-rags';\n\n// Pass load to factory, factory returns basically the same load that we can give to connect.\nconst { actions: { load }, getters: { getData } } = ragFactory({\n  load: (userId) => axios.get('/users', { userId })\n});\n\nfunction ViewUser({userId, load, user}) {\n  return (\n    <div>\n      <button onClick={() => load(userId)}>Load User Info</button>\n      {user && <UserData data={user} />}\n    </div>\n  )\n}\n\nexport default connect(state => ({ user: getData(state)}), { load })(ViewUser)\n```\n\n\nAnd that's it! That's all you need to do; no need to manually create or place a subreducer\nanywhere. The subreducer is pretty basic, it holds the return of `load` in the data attribute.\nIt also keeps track of meta information, like last load time, number of times loaded, and errors.\n\nIf you want to keep track of data by parameters passed to `load`, i.e. loading a whole bunch\nof users, you'll want to use `ragFactoryMap`. This will use `ragFactory` behind the scenes to\ncreate the simple mini-reducer for each set of parameters you specify.\n\n\n## Prerequisites\nYou'll need `redux-thunk` and to restructure your `createRootReducer` function. We'll\nneed to handle the addition of dynamic reducers!\n\nHere's what your Redux store creation will look like:\n```js\nimport { compose, combineReducers, createStore, applyMiddleware } from 'redux';\nimport thunk from 'redux-thunk';\n\nimport { combineAsyncReducers, configureRags } from 'redux-rags';\n\n// Import a few reducers like you normally would to create a top level reducer: we'll use `userReducer` as an example\nimport userReducer from './userReducer';\n\nconst createRootReducer = (dynamicReducers: Object = {}) => {\n  dynamicReducers = combineAsyncReducers(combineReducers, dynamicReducers);\n\n  return combineReducers(Object.assign({}, dynamicReducers, {\n    // Then list your reducers below\n    userReducer\n  }));\n}\n\nconst middleware = applyMiddleware(thunk);\nconst store = createStore(createRootReducer(), compose(middleware));\nconfigureRags(store, createRootReducer);\n```\n\n## What Actions and Getters are there?\nGetters take in the full store as the argument. For `ragFactory`, you'll have access to the following getters:\n\n- `get`: Returns `BoilerState<T>`, so an object that looks like `{ data, meta }`.\n- `getData`: Returns the `data` attribute from `BoilerState<T>`. It would be type `?T`, whatever your load / update functions passed to the `ragFactory` return.\n- `getMeta`: Returns the `meta` attribute from `BoilerState<T>`. This will be an object that looks like `{ loading, lastLoadTime, errors }`, with a few more properties. Look for the types / additional properties in the type definition.\n- `getIsLoading`: Returns the `loading` attribute from the `meta` field. A common use case is for quick access to the loading state, so a special getter is provided for this meta attribute.\n\nActions are implementations of commonly used features to manipulate the state. Most of these are action creators, with `load` and `update` being thunks.\n\n- `beginLoading`: Sets `loading` meta property to true.\n- `endLoading`: Sets `loading` meta property to false.\n- `reset`: Returns the subreducer to the initial state.\n- `errors`: Sets the errors meta value.\n- `clearErrors`: Clears the errors meta value.\n- `updateData`: Sets the data value for the subreducer.\n- `update`: Thunk. Calls the `update` function passed in to `ragsFactory` and sets the data value to the result. Might also set the error attribute if the `update` function throws an error. Passed in function should look like: `(dataValue, ...params) => newData`. Returned func has signature: `(...params) => newData`. This is because the current data value will be added to the function internally.\n- `load`: Thunk. Calls the appropriate sequence of `beginLoading`, `updateData`, `endLoading` actions while calling the `load` function passed to `ragsFactory`. If there are errors while executing, the error meta value will be set. Passed in function looks like: `(...params) => newData`, returned function looks like `(...params) => newData`.\n\n## Details and explanations of exports\n\n### A few types to consider:\nThe generated subreducer uses the following type for the state:\n```js\ntype BoilerState<T> = {\n  data: ?T,\n  meta: {\n    loading: boolean,\n    loaded: boolean,\n    changeCount: number,\n    lastChangeTime: ?number,\n    errors?: *\n  }\n}\n```\n|  Meta Property | Description |\n|:----:|:---:|\n| loading | Is the data currently being loaded |\n| loaded | Has the data every been loaded? Equivalent to `changeCount > 0` |\n| changeCount | Number of times the data has been loaded / updated |\n| lastChangeTime | The time of the last change |\n| errors | Error object from the `load` or `update` function, if any |\n\n### configureRags\nSignature: `(store: Object, createRootReducer: Function) => void`.\n\nThis function is how you set up the module, giving it access to your Redux store. This is used to configure the reducer\ninjector. We need `store` for the [`replaceReducer`](https://redux.js.org/api/store#replaceReducer) method. And we'll need to have a formatted `createRootReducer` function that accepts dynamic reducers. We've provided a `combineAsyncReducers` function to make this easier.\n\n### combineAsyncReducers\nSignature: `(combineReducers: Function, dynamicReducers: Object) => newDynamicReducers`\n\nThis function is designed to recursively call the `combineReducers` function on the second parameter `dynamicReducers`. This means that whatever the structure of `dynamicReducers` we can register the functions with `redux`. The package internally will add values underneath `@@redux-rags` and `@@redux-rags/map` keys in the `dynamicReducers` object. \n\n### injectReducer\nSignature: `(keys: Array<string>, reducer: Function) => void`\n\nThis function will insert the given `reducer` at the nested key location in `dynamicReducers` specified by `keys`. For example, if the user has called `ragFactory` with the name `'user info'` internally we will call this function with `injectReducer(['@@redux-rags', 'user info'], subreducer)`. This will set values so that `dynamicReducers['@@redux-rags']['user info'] == subreducer` and update the store with the newly injected subreducer.\n\nYou can call this function and inject your own subreducers. You could just use `configureRags`, `combineAsyncReducers`, and `injectReducer` to setup your store to allow for dynamic reducers and skip the rest of this module. \n\n### ragFactory\n\nThough both `load` and `update` are optional, you pass at least one. With neither of these, there is no good way to\nupdate the data or metadata fields.\n\nThe `ragFactory` will place the dynamically added subreducers in the `@@redux-rags` top leve of the Redux store.\nIf you want to place this somewhere else, just put the returned `subreducer` somewhere in your Redux store and\npass in the `getInStore` function.\n\n|  Props  |  Type  |  Optional  |  Description  |\n|:-------:|:------:|:----------:|:-------------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to Redux and the automatically injected subreducer will use that name. |\n| load | Function | yes | A function that loads data |\n| getInitialState | Function | yes | Function to create initial data for the return of `load`, will use null if not defined |\n| getInStore | Function | yes | `(state) => state.some.path.to.subreducer` Function to locate the subreducer in the store. Will place in default location if not specified, otherwise will use this location in getters. |\n| update | Function | yes | (dataValue, ...args) => { return newData; } A function to manipulate the data attribute. Returned `update` will have signature `(...args) => void` |\n| loadOnlyOnce | boolean | yes | Prevent the `load` function from being called more than once. |\n| partialReducer | Function | yes | Allows you to hook in to other actions in the Redux store and modify the state. You'll be working with the BoilerState argument. Extend the reducer and listen to other actions, for example you can could clear the data on user logout. Write this like a reducer to extend the functionality of the generated boilerplate. You can also delay assignment if you want to utilize actions in the returned Actions object, just assign the function to `subreducer.partialReducer`.|\n\n### ragFactoryMap\nWhat's this `ragFactoryMap`? Well if you want to cache data based on the query parameters, then the `ragFactoryMap` is for you!\nFor each collection of args, a new reducer will be injected. The params are stringified and used as the key for the injected subreducer.\n\nThe `ragFactoryMap` builds on top of `ragFactory`. That factory is used internally to generate the subreducers and actions for each key.\n\n| Props | Type | Optional | Description |\n|:-----:|:----:|:--------:|:-----------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to Redux and the automatically injected subreducer will use that name. |\n| load | Function | no | A function that loads data. The returned function will have the same signature. |\n| getInitialState | Function | yes | Function to create initial data for the return of load, will use null if not defined |\n\n\n## Additional Examples\n\n### Defining the Redux interactions in a separate file and importing\nWe'll use a basic fetch for this example, hitting some json endpoint. The returned load function\nhas the same signature as the function we passed in, so in this case `loadFaq` will not use\nany parameters.\n```js\nimport { ragFactory } from 'redux-rags';\n\nconst axiosLoadFaq = () => fetch('http://api.example.com/data.json');\nconst { actions, getters } = ragFactory({ load: axiosLoadFaq });\n\nexport {\n  loadFaq: actions.load,\n  getFaq: getters.getData,\n  getIsFaqLoading: getters.getIsLoading,\n};\n```\n\n#### Use that Data Request in a React Component\nImport the Getters and Action as you normally would and use them with `react-redux`. If you're used to\ndefining thunks and getters in a different file and import them, then the code below should look\nremarkably similar to code that you've already seen or even written.\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { loadFaq, getFaq, getIsFaqLoading } from './faqData';\nimport Loading from './Loading';\nimport FaqItem from './FaqItem';\n\nclass Faq extends React.Component {\n  componentDidMount() {\n    this.props.loadFaq();\n  }\n\n  render() {\n    const { isLoading, faqData } = this.props;\n    if (isLoading) {\n      return <Loading />;\n    }\n\n    return (\n      <React.Fragment>\n        {faqData.map((data, index) => <FaqItem key={index} data={data} />}\n      </React.Fragment>\n    )\n  }\n}\n\nconst mapStateFromProps = state => ({\n  faqData: getFaq(state),\n  isLoading: getIsFaqLoading(state)\n});\n\nexport default connect(mapStateFromProps, { loadFaq })(Faq);\n```\n\n### Placing the subreducer in a custom location in Redux\nConnect subreducer to your desired location in the Redux store, and tell the generator\nwhere you put it. Getters are also factories for you. If you don't care where the\nsubreducer lives, don't pass in a `getInStore` method and the generator will place it for you.\nIn this case taking `subreducer` and adding it to the `combineSubreducers` function in the `currentUser` subreducer.\nBut you can place it wherever you want, just update the `getInStore` function passed to `ragFactory`.\n```js\nimport { generator };\nconst { actions, subreducer, getters } = generator({\n  name: 'MY_DATA',\n  load: (param) => methodService.run('fetchSomething', param),\n  getInStore: (store) => store && store.currentUser && store.currentUser.my_data\n});\n```\n\n### Using update to manipulate the stored value\nIn addition to `load` you can also pass in an `update` function, which will take the\ncurrent `data` value as the first parameter. An example of this might be an update\nfunction that increments the current value by 2. The signature of the returned function\nis slightly different, as the `data` first argument is passed internally, so you pass\nin a function that takes parameters `(currentData, ...additionalParams)` and the returned\n`actions.update` function will take the parameters `(...additionalParams)`.\n[View an example that relies entirely on the `update` funciton on CodeSandbox](https://codesandbox.io/s/1307vwj8zj) \n```js\nimport { generator };\nconst { actions : { update }, getters } = generator({\n  name: 'MY_DATA',\n  update: (currentData) => currentData + 2,\n});\n```\n\n### Using the ragFactoryMap\nNot much to say, it's pretty much how you would use `ragFactory`, except treating the input arguments\nas a key for a different mini state machine.\n\n```js\n const { action: { load }, getters: { getData, getMeta } } =\n   ragFactoryMap({ load: Function });\n\n // In a component after `load` was passed in through mapDispatchToProps.\n\nclass Component extends React.Component {\n  componentDidMount() {\n    const { loaded, load, taskId, userId } = this.props;\n    // Load the data for given userId and taskId if not loaded\n    !loaded && load(userId, taskId);\n  }\n  // ... Other functions of the class\n}\n\nfunction mapStateToProps(state, props) {\n  const { userId, taskId } = props;\n  return {\n    loaded: getMeta(userId, taskId).loaded,\n    taskData: getData(userId, taskId)\n  }\n}\nexport default connect(mapStateToProps)(Component);\n```\n\n## License\nMIT\n\n## Thanks\n\nHuge thanks to Jimmy Shen for [the amazing Medium article already linked above](https://medium.com/@jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7)\non dynamically injecting reducers into the Redux store.\n","gitHead":"527fb9a11fee7c3e0bcd2dcf2bac2938345a21b7","scripts":{"flow":"flow","jest":"jest --maxWorkers=2","lint":"eslint src/**","test":"npm run jest -- --coverage --coverageReporters=text-lcov | ./node_modules/coveralls/bin/coveralls.js","build":"babel src -d build --copy-files","jest:watch":"npm run jest -- --watchAll","lint:watch":"esw -w lib/**","prepublish":"npm run build"},"_npmUser":{"name":"kyleatcodesignal","email":"kyle@codesignal.com"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.","repository":{"url":"git+https://github.com/CodeSignal/redux-rags.git","type":"git"},"_npmVersion":"5.6.0","description":"Redux Reducers, Actions, and Getters. Simplified!","directories":{},"_nodeVersion":"8.9.4","_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"jest":"^23.6.0","redux":"^4.0.1","eslint":"^5.8.0","flow-bin":"^0.86.0","coveralls":"^3.0.2","@babel/cli":"^7.2.3","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","@babel/core":"^7.2.2","redux-thunk":"^2.3.0","babel-eslint":"^10.0.1","eslint-watch":"^4.0.2","babel-polyfill":"^6.26.0","@babel/register":"^7.0.0","@babel/preset-env":"^7.3.1","@babel/preset-flow":"^7.0.0","regenerator-runtime":"^0.13.1","eslint-plugin-import":"^2.14.0","babel-plugin-transform-imports":"^1.5.1","@babel/plugin-proposal-class-properties":"^7.3.0","@babel/plugin-transform-modules-commonjs":"^7.2.0","@babel/plugin-proposal-object-rest-spread":"^7.3.1"},"_npmOperationalInternal":{"tmp":"tmp/redux-rags_1.2.1-beta3_1548724808340_0.9057624843213332","host":"s3://npm-registry-packages"}},"1.2.1":{"name":"redux-rags","version":"1.2.1","keywords":["redux"],"author":{"name":"CodeSignal"},"license":"MIT","_id":"redux-rags@1.2.1","maintainers":[{"name":"kyleatcodesignal","email":"kyle@codesignal.com"}],"homepage":"https://github.com/CodeSignal/redux-rags#readme","bugs":{"url":"https://github.com/CodeSignal/redux-rags/issues"},"dist":{"shasum":"4c9b6f83cfe797fefec9b1405870e4c75037a3f9","tarball":"https://registry.npmjs.org/redux-rags/-/redux-rags-1.2.1.tgz","fileCount":10,"integrity":"sha512-VKHFeHm1b4TOx8gNDkOsKWReeFewGp0VsyS8bboosf2HODQTZrDchy/sG9Bh+nZHpvA+fKKRTQ7mceI6cwJFrA==","signatures":[{"sig":"MEUCIGfugMjh7uFjCTPEp0YBiKkVGMUim6OgaseGdSqrGLK9AiEA6i0A+gAL771rEEBEu4zJ37M8IM2eLP7w8n4SkR9HbWA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":42604,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcT60wCRA9TVsSAnZWagAATrUQAJYum5koCJppWoRuN4ei\nfQcgssWzxlusY0RKoyWaWXvCLHjGXnnCZyEy5Es6qsGFBHubFCi1wbUYKr0m\nVsR43W0CLrN4W694auzLZzSz5/QzrddSv8WypIywaK9VHNyjOp2hB200CrlC\nGu/+7hqg572MZVSc1P+/wt3wbHjN3p6TyIhEVjhriRI7WtUBnKGMmvVgb5ui\nFoozs/2gmmhpa0z5Ieut0I0+6COGYo2oKByTpPCRAUFxr6VOAGbrS7BzHksP\nss4SjUllpnc9MOxJKaPMcAjojonsIsljhUdVru8AKlIHRfbUv3difOtHqc3+\nHxV866vNEbXU+4LNoJeWjNSCmLs9uhF+X1H8HZm4cKFfynxedfXXR5sarOnE\nqT7FNiV+ko1vKCLItEPa9N9shhKGKj9mRKKan8QxTBcQCd+XKnBOUO5qA53p\nlPd4Ge+MMzq4yYJOiq4hOLFTrh3ul1AprXE0n8vnfW4FzQzGAhHmRvXjilim\nJ6nqxhWOlT1RgrkRT8JnajZJXSocgQ7MDzJyEhzNQE9rzc9UrslNI5t2vu8C\nHYhVWh8YtM4xNSbUP9KMwmJWD4ZuQTrk6DSb7HbacgEXVvW7ZV6Ii6t4AKcH\ne3BaQpPpR+wgzR/9Z2VFVPduzOEi6PEGukmF8cWQaFXd8d9CpDg4pJ67Kfsy\nIGSi\r\n=CdjC\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/index.js","gitHead":"75d97a6f35c655ecf17dbb253a691cded9ee796e","scripts":{"flow":"flow","jest":"jest --maxWorkers=2","lint":"eslint src/**","test":"npm run jest -- --coverage --coverageReporters=text-lcov | ./node_modules/coveralls/bin/coveralls.js","build":"babel src -d build --copy-files","jest:watch":"npm run jest -- --watchAll","lint:watch":"esw -w lib/**","prepublish":"npm run build"},"_npmUser":{"name":"kyleatcodesignal","email":"kyle@codesignal.com"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.","repository":{"url":"git+https://github.com/CodeSignal/redux-rags.git","type":"git"},"_npmVersion":"5.6.0","description":"Redux Reducers, Actions, and Getters. Simplified!","directories":{},"_nodeVersion":"8.9.4","_hasShrinkwrap":false,"devDependencies":{"jest":"^23.6.0","redux":"^4.0.1","eslint":"^5.8.0","flow-bin":"^0.86.0","coveralls":"^3.0.2","@babel/cli":"^7.2.3","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","@babel/core":"^7.2.2","redux-thunk":"^2.3.0","babel-eslint":"^10.0.1","eslint-watch":"^4.0.2","babel-polyfill":"^6.26.0","@babel/register":"^7.0.0","@babel/preset-env":"^7.3.1","@babel/preset-flow":"^7.0.0","regenerator-runtime":"^0.13.1","eslint-plugin-import":"^2.14.0","babel-plugin-transform-imports":"^1.5.1","@babel/plugin-proposal-class-properties":"^7.3.0","@babel/plugin-transform-modules-commonjs":"^7.2.0","@babel/plugin-proposal-object-rest-spread":"^7.3.1"},"_npmOperationalInternal":{"tmp":"tmp/redux-rags_1.2.1_1548725551474_0.4968840895313553","host":"s3://npm-registry-packages"}},"1.2.2":{"name":"redux-rags","version":"1.2.2","keywords":["redux"],"author":{"name":"CodeSignal"},"license":"MIT","_id":"redux-rags@1.2.2","maintainers":[{"name":"albert96","email":"albert@codesignal.com"},{"name":"albinutte","email":"albina.ezus+npm@gmail.com"},{"name":"artahian","email":"artahian@gmail.com"},{"name":"daudadagr","email":"michaeleugenenewman@gmail.com"},{"name":"kyleatcodesignal","email":"kyle@codesignal.com"}],"homepage":"https://github.com/CodeSignal/redux-rags#readme","bugs":{"url":"https://github.com/CodeSignal/redux-rags/issues"},"dist":{"shasum":"661d58dc7a0b7b3c7dc97617da202446e05f100a","tarball":"https://registry.npmjs.org/redux-rags/-/redux-rags-1.2.2.tgz","fileCount":10,"integrity":"sha512-xxDYaUTRCcvG4SwNzFFX4WW5DbgUidgTl0X34VlT4nD1ja+k4lWpqs0Kpz8FqzevjAWBPaAxIydjnjGXOQnY4w==","signatures":[{"sig":"MEQCIFGOqnOnXrNhPcNCSI75calef3ygTqhqIrYE59VPujiMAiA0ZOsdLJ7FZTvNisuqwMuYmlSUuOsCS86REPSvf5LijA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":43070,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdUZ+qCRA9TVsSAnZWagAA4/IQAKTfglNZSqjWdXgwi3Kj\nQWYEBFPp1b39naNmscpjZsEWxX52/OlAuprSnivVwQZY0s9LuCdSNSj5Szbf\n32zgw44XLuRWGD+Hm8HmLspoH9EJDAnNFZr7J0DNgcw4tB0Ynk+icyNLXbul\nFiwFbb4OAYw59WO5xvmhR0CwkY7TwKWPFkvHfHgEbVhh78/LbUv7i48D8uAs\nLQ3nMKBstUHrNujWOhk/D67FvCFowiP09yVzlo9MJaKWAgNJvnxt7w09nUtj\nOo7+n1gRe5o4hyPZYl0fSr/K7/1O5Ka8wg4mHrjjTIsbz3LQb5RFR8NAZIxE\nGZCKolOB6+kr2CwptgU3Pt4pn/j4X/kQHt9SKt3pCjf2GzEVPT7R6F4RuMgR\nkpT+XV1iGXDHoshZUsDUjA7KPgwEu3L81OXVEg5vHeIlWhnxID9Lni4boNZo\nUM3X4TFbje+kBRTNLOlh/WZnCA9sHDW5ofi3XpKTeNA4i/swGsC6dhapxoDA\najTAYCR3QUPU5Cxa4eKdCrhZ9n4DzwLigpYcoakIwhMQOhVx4BHjV3ZttQkV\nJwVibqSaBhfjH+7KJgTErThcsPv+P3BBXPpm+v49HEtvv4oek6haSjwAASHj\nCJku/4dG2HxUuuNYonGiYtq92w7WvmgVbVgtdgBuI6/dtxneizd6uOxaGLHD\n0bcT\r\n=8B1o\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/index.js","gitHead":"d7447328222dba64d9f4f91bb41e089d7ae40775","scripts":{"flow":"flow","jest":"jest --maxWorkers=2","lint":"eslint src/**","test":"npm run jest -- --coverage --coverageReporters=text-lcov | ./node_modules/coveralls/bin/coveralls.js","build":"babel src -d build --copy-files","jest:watch":"npm run jest -- --watchAll","lint:watch":"esw -w lib/**","prepublish":"npm run build"},"_npmUser":{"name":"daudadagr","email":"michaeleugenenewman@gmail.com"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.","repository":{"url":"git+https://github.com/CodeSignal/redux-rags.git","type":"git"},"_npmVersion":"6.4.1","description":"Redux Reducers, Actions, and Getters. Simplified!","directories":{},"_nodeVersion":"10.15.3","_hasShrinkwrap":false,"devDependencies":{"jest":"^24.8.0","redux":"^4.0.4","eslint":"^5.16.0","flow-bin":"^0.87.0","coveralls":"^3.0.5","@babel/cli":"^7.5.5","babel-core":"^7.0.0-bridge.0","babel-jest":"^24.8.0","@babel/core":"^7.5.5","redux-thunk":"^2.3.0","babel-eslint":"^10.0.2","eslint-watch":"^5.1.2","babel-polyfill":"^6.26.0","@babel/register":"^7.5.5","@babel/preset-env":"^7.5.5","@babel/preset-flow":"^7.0.0","regenerator-runtime":"^0.13.3","eslint-plugin-import":"^2.18.2","babel-plugin-transform-imports":"^1.5.1","@babel/plugin-proposal-class-properties":"^7.5.5","@babel/plugin-transform-modules-commonjs":"^7.5.0","@babel/plugin-proposal-object-rest-spread":"^7.5.5"},"_npmOperationalInternal":{"tmp":"tmp/redux-rags_1.2.2_1565630377574_0.8376160317803925","host":"s3://npm-registry-packages"}},"1.2.3":{"name":"redux-rags","version":"1.2.3","keywords":["redux"],"author":{"name":"CodeSignal"},"license":"MIT","_id":"redux-rags@1.2.3","maintainers":[{"name":"albert96","email":"albert@codesignal.com"},{"name":"albinutte","email":"albina.ezus+npm@gmail.com"},{"name":"artahian","email":"artahian@gmail.com"},{"name":"daudadagr","email":"michaeleugenenewman@gmail.com"},{"name":"kyleatcodesignal","email":"kyle@codesignal.com"}],"homepage":"https://github.com/CodeSignal/redux-rags#readme","bugs":{"url":"https://github.com/CodeSignal/redux-rags/issues"},"dist":{"shasum":"459e1251247ec3e541ef22e246ec5b5fca3ba581","tarball":"https://registry.npmjs.org/redux-rags/-/redux-rags-1.2.3.tgz","fileCount":10,"integrity":"sha512-ub2qFFyWHkZuqMfc0sq4xBs8dASmYMncM+SmMX47tYhcjt7f045s9qsd5gsKE03s4As/RFVdda2l/61cxzHHNQ==","signatures":[{"sig":"MEUCIBv5E1y+c1Lbu868/cS+9+UJzvjcsRQw0lr8/0nC+YoeAiEAh75WFUap6F3q0p+QBLOUyPBe9rviNBYqG5T1KOXVtWo=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":43910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd1wT3CRA9TVsSAnZWagAA3yQP/icjB6Y51+lxjSwOuKkT\nFYor23bB6j9+jKlALxSKvTMtMyXybc2FGMMBL6+432wVd0cKgattIYwFVauY\nxJd+QMEWNpLW9+sU5IGHSvTPjZooHkwi0TSloisAQqXQdXY4kCPT0WpzGRIc\ns+TBSPMCcb6KlDRQUMDcF0M7hucGmmrgXrZxVFoVMz88uRvlsq/CpYDwCUcj\n0RlAd17R0rPnFZjWAvaSbpJ8MY3yHLn/lhJoQINHhOJAvi3yLEOCBbCovgTg\nGJC8fXFXrdissBp8dQp/mHi/9XgLaTD+Du/ddq1Bu5YJVbB2uf2jXv7iEtM/\nI/4zg3A47+QBZrwWrau4ozecnKRjuJP/pcjo6cP0XdjUtHCaQIY3lEg2kXBD\nK3YxHLHC0Zfavh7PJFbvU0AxYcccEDRD2FFs4gepj+8EW2/5nU9Zk3wSYPyv\nHsr4iRBhxCPDZFAc2TxpV+saPk5ffJmv5RCYiWbRNeBkxxPD0NSgDnDthr4T\nPP0IbPbdAHFBDnPJSYpHPFTRMaI1vt7HfkKL5Nfu26H03nFcatveMyt10nBG\n/bt1zoHrcy7ftXv5/YKVFLL5iXEdSKo6ccO3uNOoY0pseNYlrg8RzgYaMVM9\nFdtwS5dQ8Ew1jQWPkyilRfs3Nmrz5hGMf83S26SBDFwbEfEzwg3j5qUJDKQC\nRUQ4\r\n=eFug\r\n-----END PGP SIGNATURE-----\r\n"},"main":"build/index.js","gitHead":"da1cc4c8c6e386d6e3906c21740e41441c2bebd9","scripts":{"flow":"flow","jest":"jest --maxWorkers=2","lint":"eslint src/**","test":"npm run jest -- --coverage --coverageReporters=text-lcov | ./node_modules/coveralls/bin/coveralls.js","build":"babel src -d build --copy-files","jest:watch":"npm run jest -- --watchAll","lint:watch":"esw -w lib/**","prepublish":"npm run build"},"_npmUser":{"name":"albinutte","email":"albina.ezus+npm@gmail.com"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.","repository":{"url":"git+https://github.com/CodeSignal/redux-rags.git","type":"git"},"_npmVersion":"6.9.0","description":"Redux Reducers, Actions, and Getters. Simplified!","directories":{},"_nodeVersion":"10.16.3","_hasShrinkwrap":false,"devDependencies":{"jest":"^24.8.0","redux":"^4.0.4","eslint":"^5.16.0","flow-bin":"^0.87.0","coveralls":"^3.0.5","@babel/cli":"^7.5.5","babel-core":"^7.0.0-bridge.0","babel-jest":"^24.8.0","@babel/core":"^7.5.5","redux-thunk":"^2.3.0","babel-eslint":"^10.0.2","eslint-watch":"^5.1.2","babel-polyfill":"^6.26.0","@babel/register":"^7.5.5","@babel/preset-env":"^7.5.5","@babel/preset-flow":"^7.0.0","regenerator-runtime":"^0.13.3","eslint-plugin-import":"^2.18.2","babel-plugin-transform-imports":"^2.0.0","@babel/plugin-proposal-class-properties":"^7.5.5","@babel/plugin-transform-modules-commonjs":"^7.5.0","@babel/plugin-proposal-object-rest-spread":"^7.5.5"},"_npmOperationalInternal":{"tmp":"tmp/redux-rags_1.2.3_1574372599370_0.3024018106305537","host":"s3://npm-registry-packages"}}},"time":{"created":"2018-11-12T18:14:13.885Z","modified":"2026-07-07T16:07:36.554Z","0.0.1":"2018-11-12T18:14:14.046Z","0.1.0":"2018-11-12T18:25:44.517Z","0.1.0-rc1":"2018-11-12T18:32:59.661Z","1.0.0":"2018-11-12T18:36:35.450Z","1.0.1":"2018-11-13T02:25:57.638Z","1.1.0":"2018-12-02T21:55:19.661Z","1.1.1":"2019-01-19T02:35:17.797Z","1.2.0-beta":"2019-01-22T18:32:37.344Z","1.2.0-beta1":"2019-01-22T20:00:44.883Z","1.2.0-beta2":"2019-01-22T20:17:08.257Z","1.2.0":"2019-01-22T20:24:11.127Z","1.2.1-beta":"2019-01-28T18:07:52.138Z","1.2.1-beta2":"2019-01-28T18:14:51.419Z","1.2.1-beta3":"2019-01-29T01:20:08.504Z","1.2.1":"2019-01-29T01:32:31.606Z","1.2.2":"2019-08-12T17:19:37.671Z","1.2.3":"2019-11-21T21:43:19.470Z"},"bugs":{"url":"https://github.com/CodeSignal/redux-rags/issues"},"author":{"name":"CodeSignal"},"license":"MIT","homepage":"https://github.com/CodeSignal/redux-rags#readme","keywords":["redux"],"repository":{"url":"git+https://github.com/CodeSignal/redux-rags.git","type":"git"},"description":"Redux Reducers, Actions, and Getters. Simplified!","maintainers":[{"email":"albina.ezus+npm@gmail.com","name":"albinutte"},{"email":"michaeleugenenewman@gmail.com","name":"daudadagr"},{"email":"albert@codesignal.com","name":"albert96"},{"email":"arthur.hoareau@codesignal.com","name":"arthurhoaro"},{"email":"dev@codesignal.com","name":"botty-mcbotface"}],"readme":"# Redux-RAGs\n[![npm version](https://img.shields.io/npm/v/redux-rags.svg?style=flat-square)](https://www.npmjs.com/package/redux-rags)\n[![npm downloads](https://img.shields.io/npm/dm/redux-rags.svg?style=flat-square)](https://www.npmjs.com/package/redux-rags)\n[![Coverage Status](https://coveralls.io/repos/github/CodeSignal/redux-rags/badge.svg?branch=master)](https://coveralls.io/github/CodeSignal/redux-rags?branch=master)\n\nRedux **R**educers, **A**ctions, and **G**etters: Simplified!\n\nTLDR: No need to create or connect subreducers for simple async requests. Your Redux connection is handled by a single function\n```js\nconst rag = ragFactory({ load });\n\nconst newLoad = rag.actions.load;\nconst getData = rag.getters.getData;\nconnect(state => ({ data: getData(store) }), { load: newLoad })(Component);\n```\n\n## Motivation\nWe found that a lot of our reducers were redefining a common theme. We have some endpoint\nthat we want to query for data. We'd run through the same steps every time:\n* Begin loading state\n* Fetch Data\n* End Loading State\n* Set data or error\n\n<center>\n  <img src=\"media/data-flow.png\" width=\"80%\" />\n</center>\n\nAnd that's it: A mini state machine that we redefined over and over, creating dozens of\nsimilar actions and subreducers. But what if there were an easier way? What if we could\ndefine that mini state machine once and re-use the logic with each kind of query? Well `redux-rags` is\nhere to help!\n\n## Injecting a Subreducer?!\nUsing Redux requires quite a lot of boilerplate. You need to create Actions and maybe Action Creators. You need to create Reducers and place them somewhere in the store, maybe as a part of another reducer. There are a myriad of tools out there to help you simplify this process, and we're adding another to the list! This one is built around the idea that there are some pieces of state that you would like to have stored outside of a particular rendering context, but don't want to go through all the migration work to create a custom reducer in the store for this data. You want to take advantage of the benefits of Redux without trudging through all the boilerplate.\n\nSo what if you had a way to add subreducers to Redux? Then you could write code that generates the subreducer structures you use frequently and inject them! You could now start writing subreducers aimed at general cases of data interaction and vastly reduce the time it takes to migrate a piece of state to Redux. We've provided our most useful abstraction here, a simple data request lifecycle. Hopefully you can see the opportunities that injecting a subreducer provides and can use the code here as an example for how to start reusing your own subreducer logic.\n\nFor more details, I encourage you to check out [this Medium article by Jimmy Shen on subreducer injection.](https://medium.com/@jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7)\n\n## Usage\n\n\n### Single File Redux Connection Example\nHere's how you'd interact with Redux for a data request. We're going to create the actions and\nautomatically create and inject the subreducer. The returned `load` function will take the same\narguments as the `load` function passed in, so you have control over the `load` thunk.\n\nHere we want to hit an endpoint with the `userId` parameter. We'll avoid worrying about\nloading states or anything for now. The call to `ragFactory` creates the actions and injects\nthe subreducer for us. Then this state information is stored in Redux, so when users return to the\ncomponent they'll see the cached data. [Play with a similar example on CodeSandbox](https://codesandbox.io/s/k218x138l7)\n\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { ragFactory } from 'redux-rags';\n\n// Pass load to factory, factory returns basically the same load that we can give to connect.\nconst { actions: { load }, getters: { getData } } = ragFactory({\n  load: (userId) => axios.get('/users', { userId })\n});\n\nfunction ViewUser({userId, load, user}) {\n  return (\n    <div>\n      <button onClick={() => load(userId)}>Load User Info</button>\n      {user && <UserData data={user} />}\n    </div>\n  )\n}\n\nexport default connect(state => ({ user: getData(state)}), { load })(ViewUser)\n```\n\n\nAnd that's it! That's all you need to do; no need to manually create or place a subreducer\nanywhere. The subreducer is pretty basic, it holds the return of `load` in the data attribute.\nIt also keeps track of meta information, like last load time, number of times loaded, and errors.\n\nIf you want to keep track of data by parameters passed to `load`, i.e. loading a whole bunch\nof users, you'll want to use `ragFactoryMap`. This will use `ragFactory` behind the scenes to\ncreate the simple mini-reducer for each set of parameters you specify.\n\n\n## Prerequisites\nYou'll need `redux-thunk` and to restructure your `createRootReducer` function. We'll\nneed to handle the addition of dynamic reducers!\n\nHere's what your Redux store creation will look like:\n```js\nimport { compose, combineReducers, createStore, applyMiddleware } from 'redux';\nimport thunk from 'redux-thunk';\n\nimport { combineAsyncReducers, configureRags } from 'redux-rags';\n\n// Import a few reducers like you normally would to create a top level reducer: we'll use `userReducer` as an example\nimport userReducer from './userReducer';\n\nconst createRootReducer = (dynamicReducers: Object = {}) => {\n  dynamicReducers = combineAsyncReducers(combineReducers, dynamicReducers);\n\n  return combineReducers(Object.assign({}, dynamicReducers, {\n    // Then list your reducers below\n    userReducer\n  }));\n}\n\nconst middleware = applyMiddleware(thunk);\nconst store = createStore(createRootReducer(), compose(middleware));\nconfigureRags(store, createRootReducer);\n```\n\n## What Actions and Getters are there?\nGetters take in the full store as the argument. For `ragFactory`, you'll have access to the following getters:\n\n- `get`: Returns `BoilerState<T>`, so an object that looks like `{ data, meta }`.\n- `getData`: Returns the `data` attribute from `BoilerState<T>`. It would be type `?T`, whatever your load / update functions passed to the `ragFactory` return.\n- `getMeta`: Returns the `meta` attribute from `BoilerState<T>`. This will be an object that looks like `{ loading, lastLoadTime, errors }`, with a few more properties. Look for the types / additional properties in the type definition.\n- `getIsLoading`: Returns the `loading` attribute from the `meta` field. A common use case is for quick access to the loading state, so a special getter is provided for this meta attribute.\n\nActions are implementations of commonly used features to manipulate the state. Most of these are action creators, with `load` and `update` being thunks.\n\n- `beginLoading`: Sets `loading` meta property to true.\n- `endLoading`: Sets `loading` meta property to false.\n- `reset`: Returns the subreducer to the initial state.\n- `errors`: Sets the errors meta value.\n- `clearErrors`: Clears the errors meta value.\n- `updateData`: Sets the data value for the subreducer.\n- `update`: Thunk. Calls the `update` function passed in to `ragsFactory` and sets the data value to the result. Might also set the error attribute if the `update` function throws an error. Passed in function should look like: `(dataValue, ...params) => newData`. Returned func has signature: `(...params) => newData`. This is because the current data value will be added to the function internally.\n- `load`: Thunk. Calls the appropriate sequence of `beginLoading`, `updateData`, `endLoading` actions while calling the `load` function passed to `ragsFactory`. If there are errors while executing, the error meta value will be set. Passed in function looks like: `(...params) => newData`, returned function looks like `(...params) => newData`.\n\n## Details and explanations of exports\n\n### A few types to consider:\nThe generated subreducer uses the following type for the state:\n```js\ntype BoilerState<T> = {\n  data: ?T,\n  meta: {\n    loading: boolean,\n    loaded: boolean,\n    changeCount: number,\n    lastChangeTime: ?number,\n    errors?: *\n  }\n}\n```\n|  Meta Property | Description |\n|:----:|:---:|\n| loading | Is the data currently being loaded |\n| loaded | Has the data every been loaded? Equivalent to `changeCount > 0` |\n| changeCount | Number of times the data has been loaded / updated |\n| lastChangeTime | The time of the last change |\n| errors | Error object from the `load` or `update` function, if any |\n\n### configureRags\nSignature: `(store: Object, createRootReducer: Function) => void`.\n\nThis function is how you set up the module, giving it access to your Redux store. This is used to configure the reducer\ninjector. We need `store` for the [`replaceReducer`](https://redux.js.org/api/store#replaceReducer) method. And we'll need to have a formatted `createRootReducer` function that accepts dynamic reducers. We've provided a `combineAsyncReducers` function to make this easier.\n\n### combineAsyncReducers\nSignature: `(combineReducers: Function, dynamicReducers: Object) => newDynamicReducers`\n\nThis function is designed to recursively call the `combineReducers` function on the second parameter `dynamicReducers`. This means that whatever the structure of `dynamicReducers` we can register the functions with `redux`. The package internally will add values underneath `@@redux-rags` and `@@redux-rags/map` keys in the `dynamicReducers` object. \n\n### injectReducer\nSignature: `(keys: Array<string>, reducer: Function) => void`\n\nThis function will insert the given `reducer` at the nested key location in `dynamicReducers` specified by `keys`. For example, if the user has called `ragFactory` with the name `'user info'` internally we will call this function with `injectReducer(['@@redux-rags', 'user info'], subreducer)`. This will set values so that `dynamicReducers['@@redux-rags']['user info'] == subreducer` and update the store with the newly injected subreducer.\n\nYou can call this function and inject your own subreducers. You could just use `configureRags`, `combineAsyncReducers`, and `injectReducer` to setup your store to allow for dynamic reducers and skip the rest of this module. \n\n### ragFactory\n\nThough both `load` and `update` are optional, you pass at least one. With neither of these, there is no good way to\nupdate the data or metadata fields.\n\nThe `ragFactory` will place the dynamically added subreducers in the `@@redux-rags` top leve of the Redux store.\nIf you want to place this somewhere else, just put the returned `subreducer` somewhere in your Redux store and\npass in the `getInStore` function.\n\n|  Props  |  Type  |  Optional  |  Description  |\n|:-------:|:------:|:----------:|:-------------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to Redux and the automatically injected subreducer will use that name. |\n| load | Function | yes | A function that loads data |\n| getInitialState | Function | yes | Function to create initial data for the return of `load`, will use null if not defined |\n| getInStore | Function | yes | `(state) => state.some.path.to.subreducer` Function to locate the subreducer in the store. Will place in default location if not specified, otherwise will use this location in getters. |\n| update | Function | yes | (dataValue, ...args) => { return newData; } A function to manipulate the data attribute. Returned `update` will have signature `(...args) => void` |\n| loadOnlyOnce | boolean | yes | Prevent the `load` function from being called more than once. |\n| partialReducer | Function | yes | Allows you to hook in to other actions in the Redux store and modify the state. You'll be working with the BoilerState argument. Extend the reducer and listen to other actions, for example you can could clear the data on user logout. Write this like a reducer to extend the functionality of the generated boilerplate. You can also delay assignment if you want to utilize actions in the returned Actions object, just assign the function to `subreducer.partialReducer`.|\n\n### ragFactoryMap\nWhat's this `ragFactoryMap`? Well if you want to cache data based on the query parameters, then the `ragFactoryMap` is for you!\nFor each collection of args, a new reducer will be injected. The params are stringified and used as the key for the injected subreducer.\n\nThe `ragFactoryMap` builds on top of `ragFactory`. That factory is used internally to generate the subreducers and actions for each key.\n\n| Props | Type | Optional | Description |\n|:-----:|:----:|:--------:|:-----------:|\n| name | string | yes | A string that identifies the data. If you pass a name, the actions sent to Redux and the automatically injected subreducer will use that name. |\n| load | Function | no | A function that loads data. The returned function will have the same signature. |\n| getInitialState | Function | yes | Function to create initial data for the return of load, will use null if not defined |\n\n\n## Additional Examples\n\n### Defining the Redux interactions in a separate file and importing\nWe'll use a basic fetch for this example, hitting some json endpoint. The returned load function\nhas the same signature as the function we passed in, so in this case `loadFaq` will not use\nany parameters.\n```js\nimport { ragFactory } from 'redux-rags';\n\nconst axiosLoadFaq = () => fetch('http://api.example.com/data.json');\nconst { actions, getters } = ragFactory({ load: axiosLoadFaq });\n\nexport {\n  loadFaq: actions.load,\n  getFaq: getters.getData,\n  getIsFaqLoading: getters.getIsLoading,\n};\n```\n\n#### Use that Data Request in a React Component\nImport the Getters and Action as you normally would and use them with `react-redux`. If you're used to\ndefining thunks and getters in a different file and import them, then the code below should look\nremarkably similar to code that you've already seen or even written.\n```js\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { loadFaq, getFaq, getIsFaqLoading } from './faqData';\nimport Loading from './Loading';\nimport FaqItem from './FaqItem';\n\nclass Faq extends React.Component {\n  componentDidMount() {\n    this.props.loadFaq();\n  }\n\n  render() {\n    const { isLoading, faqData } = this.props;\n    if (isLoading) {\n      return <Loading />;\n    }\n\n    return (\n      <React.Fragment>\n        {faqData.map((data, index) => <FaqItem key={index} data={data} />}\n      </React.Fragment>\n    )\n  }\n}\n\nconst mapStateFromProps = state => ({\n  faqData: getFaq(state),\n  isLoading: getIsFaqLoading(state)\n});\n\nexport default connect(mapStateFromProps, { loadFaq })(Faq);\n```\n\n### Placing the subreducer in a custom location in Redux\nConnect subreducer to your desired location in the Redux store, and tell the generator\nwhere you put it. Getters are also factories for you. If you don't care where the\nsubreducer lives, don't pass in a `getInStore` method and the generator will place it for you.\nIn this case taking `subreducer` and adding it to the `combineSubreducers` function in the `currentUser` subreducer.\nBut you can place it wherever you want, just update the `getInStore` function passed to `ragFactory`.\n```js\nimport { generator };\nconst { actions, subreducer, getters } = generator({\n  name: 'MY_DATA',\n  load: (param) => methodService.run('fetchSomething', param),\n  getInStore: (store) => store && store.currentUser && store.currentUser.my_data\n});\n```\n\n### Using update to manipulate the stored value\nIn addition to `load` you can also pass in an `update` function, which will take the\ncurrent `data` value as the first parameter. An example of this might be an update\nfunction that increments the current value by 2. The signature of the returned function\nis slightly different, as the `data` first argument is passed internally, so you pass\nin a function that takes parameters `(currentData, ...additionalParams)` and the returned\n`actions.update` function will take the parameters `(...additionalParams)`.\n[View an example that relies entirely on the `update` funciton on CodeSandbox](https://codesandbox.io/s/1307vwj8zj) \n```js\nimport { generator };\nconst { actions : { update }, getters } = generator({\n  name: 'MY_DATA',\n  update: (currentData) => currentData + 2,\n});\n```\n\n### Using the ragFactoryMap\nNot much to say, it's pretty much how you would use `ragFactory`, except treating the input arguments\nas a key for a different mini state machine.\n\n```js\n const { action: { load }, getters: { getData, getMeta } } =\n   ragFactoryMap({ load: Function });\n\n // In a component after `load` was passed in through mapDispatchToProps.\n\nclass Component extends React.Component {\n  componentDidMount() {\n    const { loaded, load, taskId, userId } = this.props;\n    // Load the data for given userId and taskId if not loaded\n    !loaded && load(userId, taskId);\n  }\n  // ... Other functions of the class\n}\n\nfunction mapStateToProps(state, props) {\n  const { userId, taskId } = props;\n  return {\n    loaded: getMeta(userId, taskId).loaded,\n    taskData: getData(userId, taskId)\n  }\n}\nexport default connect(mapStateToProps)(Component);\n```\n\n## License\nMIT\n\n## Thanks\n\nHuge thanks to Jimmy Shen for [the amazing Medium article already linked above](https://medium.com/@jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7)\non dynamically injecting reducers into the Redux store.\n","readmeFilename":"README.md"}