{"id":15603327,"url":"https://github.com/codemeasandwich/redux-auto","last_synced_at":"2025-04-14T00:22:42.453Z","repository":{"id":57350277,"uuid":"91911725","full_name":"codemeasandwich/redux-auto","owner":"codemeasandwich","description":"Automatically generate Redux stories and actions from your folder and file structure","archived":false,"fork":false,"pushed_at":"2023-11-28T04:04:28.000Z","size":806,"stargazers_count":45,"open_issues_count":26,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-27T14:21:56.805Z","etag":null,"topics":["asynchronous","react","react-native","redux","redux-auto","state-management","webapp"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/codemeasandwich.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-05-20T19:31:34.000Z","updated_at":"2023-08-03T09:49:09.000Z","dependencies_parsed_at":"2024-11-01T00:03:15.716Z","dependency_job_id":"1290efb7-e85a-48f5-b9db-249c61999b49","html_url":"https://github.com/codemeasandwich/redux-auto","commit_stats":{"total_commits":147,"total_committers":3,"mean_commits":49.0,"dds":0.5442176870748299,"last_synced_commit":"76315d74d8def57934f279e0b2d300eb8a395b9a"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codemeasandwich%2Fredux-auto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codemeasandwich%2Fredux-auto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codemeasandwich%2Fredux-auto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codemeasandwich%2Fredux-auto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/codemeasandwich","download_url":"https://codeload.github.com/codemeasandwich/redux-auto/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248448188,"owners_count":21105255,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asynchronous","react","react-native","redux","redux-auto","state-management","webapp"],"created_at":"2024-10-03T03:02:42.314Z","updated_at":"2025-04-14T00:22:42.401Z","avatar_url":"https://github.com/codemeasandwich.png","language":"JavaScript","funding_links":["https://www.buymeacoffee.com/codemeasandwich"],"categories":[],"sub_categories":[],"readme":"![redux-auto][logo]\n\n[logo]: https://s3-eu-west-1.amazonaws.com/redux-auto/reduxautologo.png \"redux-auto logo\"\n\n## Redux made easy (with a plug and play approach)\n##### Removing the boilerplate code in setting up a store \u0026 actions\n\n[![npm version](https://badge.fury.io/js/redux-auto.svg)](https://www.npmjs.com/package/redux-auto)\n[![npm downloads](https://img.shields.io/npm/dt/redux-auto.svg)](http://www.npmtrends.com/redux-auto)\n[![Build Status](https://travis-ci.org/codemeasandwich/redux-auto.svg?branch=master)](https://travis-ci.org/codemeasandwich/redux-auto)\n![gzip size](http://img.badgesize.io/https://cdn.jsdelivr.net/npm/redux-auto@latest/bundle.min.js?compression=gzip)\n[![Coverage Status](https://coveralls.io/repos/github/codemeasandwich/redux-auto/badge.svg?branch=master)](https://coveralls.io/github/codemeasandwich/redux-auto?branch=master)\n[![Known Vulnerabilities](https://snyk.io/test/npm/redux-auto/badge.svg)](https://snyk.io/test/npm/redux-auto)\n[![compatible with preact-redux](https://img.shields.io/badge/compatible%20with-preact--redux-673ab8.svg)](https://github.com/developit/preact-redux)\n[![compatible with reselect](https://img.shields.io/badge/compatible%20with-reselect-8ba376.svg)](https://www.npmjs.com/package/red-ux#redux-result-caching-with-genselectstate)\n[![Cookbook](https://img.shields.io/badge/coding%20cookbook-📖-yellowgreen.svg)](https://github.com/codemeasandwich/redux-auto/blob/master/cookbook.md)\n [![Buy me a coffee](https://img.shields.io/badge/buy%20me-a%20coffee-orange.svg)](https://www.buymeacoffee.com/codemeasandwich)\n\n## why\nI created this utility to allow you to get up and running with Redux in a fraction of the time!\n\n### Plug \u0026 Play\n* No wiring together of actions \u0026 reduces\n* No hardcoding actions types\n* No action creator or dispatcher to worry about\n* Easy Async for calling APIs\n* Server-side rendering - with all or partial store generation\n* Easy *initialize* for parts of your store\n* Easy install = works as the same as other redux middleware\n* Pure JS no external dependencies!\n* SuperSmall: 3k (minify + gzip)\n\n#### Have an existing project? No worries. Drop it in, to work along side the traditional redux way.\n\n# [Live Demo](https://bit.ly/redux-auto-demo) / [Demo Source](https://bit.ly/redux-auto-example)\n\n### If you like it, [★ it on github](https://bit.ly/redux-auto-star) and share  :beers:\n\n  * [Why](#why)\n    + [plug \u0026 Play](#plug--play)\n    + [asynchronous](#asynchronous)\n- [Live Demo](https://bit.ly/redux-auto-demo)\n  * [Source](https://bit.ly/redux-auto-example)\n- [Overview](#overview)\n- [setup](#setup)\n  * [Using along side an existing Redux setup](#using-along-side-an-existing-redux-setup)\n  * [Using along side other Redux middleware](#using-along-side-other-redux-middleware---web-app-)\n  * [Actions are available in the UI](#actions-are-available-in-the-ui)\n- [Action files](#action-files)\n  * [Chaining action together](#chaining-actions-together)\n    * [call dispatcher](#chaining-to-dispatcher)\n  * [Cancel an action](#cancel-an-action)\n- [Index files](#index-files)\n  * [before](#before)\n  * [logic](#default)\n  * [after](#after)\n  * [listening for other actions](#listening-for-other-actions)\n- [handling async actions in your ui](#handling-async-actions-in-your-ui)\n- [smart actions](#smart-actions)\n- [testing](#testing)\n- [resources](#resources)\n\n#### For Pro tips, don't forget to checkout the [![Cookbook](https://img.shields.io/badge/coding%20cookbook-📖-yellowgreen.svg)](https://github.com/codemeasandwich/redux-auto/blob/master/cookbook.md)\n\n---\n\n### asynchronous\n\nIn Redux your reducer returns a state object. This is very straight forward, but makes dealing with asynchronous updates quite tricky (there are [more than 60 different libraries](https://github.com/markerikson/redux-ecosystem-links/blob/master/side-effects.md) tackling this problem).\n\nredux-auto fixes this asynchronous problem simply by allowing you to create an [\"action\" function that returns a promise](#action-files). To accompany your \"default\" function action logic.\n\n\u003e [asynchronous: example](https://github.com/codemeasandwich/redux-auto/blob/master/example/store/user/init.js)\n\n1) No need for other Redux async middleware. e.g. thunk, promise-middleware, saga\n2) Easily allows you to pass a promise into redux and have it managed for you\n3) Allows you to co-locate external service calls with where they will be transformed\n4) Naming the file \"**init**.js\" will have it called once at app start. This is good for loading data from the server to warm up you client cache.\n\n## Overview\n\nSteps:\n\n1) Create a folder to represent your store\n\t* This is where the data, logic \u0026 flow control of the application lives. This can be named whatever, just point to it with webpacks - require.context\n2) In this folder you will create folders to represent each attribute on the store\n\t* For example. the \"user\" folder will create an attribute of 'user'\n\t* the JS files within the folder are **actions** that can be fired to change the shape of user.\n3) Create an index.js file to set default values\n\t* **[export default](#default)** is a catch all reducer function *(if an action cant be found)*\n\t* export \"[before](#before)\" \u0026 \"[after](#after)\" as lifecycle functions\n4) Create js files with the name of the [action](#action-files) you want it mapped to\n\t* **export default** is the reducer function\n\t* export \"action\" function. Is an **action-middleware** that will allow you to create promises\n5) You can create an init.js It will be automatically run once after store created\n\t* using this to initialize from an API\n\nExample layout:\n```\n└── store/ (1)\n    └──user/ (2)\n       └── index.js (3)\n       └── changeName.js (4)\n       └── init.js (5)\n```\n\n\n## setup\n\n[Example of setup file](https://github.com/codemeasandwich/redux-auto/blob/master/example/main.jsx)\n\n### Inside your setup file **Web-App*\n\n```JS\n...\nimport { createStore, applyMiddleware, combineReducers } from 'redux';\nimport { auto, reducers } from 'redux-auto';\n...\n// load the folder that hold you store\nconst webpackModules = require.context(\"./store\", true, /\\.js$/);\n...\n                                // build 'auto' based on target files via Webpack\nconst middleware = applyMiddleware( auto(webpackModules, webpackModules.keys()))\nconst store = createStore(combineReducers(reducers), middleware );\n...\n```\n\n###  Inside your API file **Server-side Rendering*\n```JS\n...\nimport React from 'react'\nimport ReactDOMServer from 'react-dom/server\nimport { genStore, fsModules } from 'redux-auto';\nimport Main from './Main';\n...\nconst webpackModules = fsModules(\"./store\")\n...\napp.get('/', function (req, res) {\n    // Only load \"user\" in store and timeout 5 sec\n    genStore(webpackModules, [\"user\"], 5000)\n      .then( store =\u003e {\n            res.send(ReactDOMServer.renderToString(\u003cMain store={store} /\u003e)))\n       }).catch( err =\u003e {\n            // check your init promise are completing\n           res.status(500).send(\"Problem in getting your page\");\n       })\n})\n...\n```\n\n### Inside your setup file **React-Native*\n\n➡ If you want to use Redux-auto in a **React-Native project**. You will just need to install the [babel-plugin-redux-auto](https://www.npmjs.com/package/babel-plugin-redux-auto) to allow to dynamic importing of your store.\n1. `npm i babel-plugin-redux-auto`\n2. Add **'babel-plugin-redux-auto'** to your **plugins** within your babel config\n\nNow back to the setup...\n\n```JS\n...\nimport { createStore, applyMiddleware, combineReducers } from 'redux';\nimport { auto, reducers } from 'redux-auto';\n...\n// load the folder that hold you store\nimport nativeStore from './store/*'\n...\nconst middleware = applyMiddleware( auto(nativeStore))\nconst store = createStore(combineReducers(reducers), middleware );\n...\n```\n\n### Using along side an existing Redux setup\n\n```JS\n...\n// import your exiting reducers\nimport reducers from './reducers';\n// include mergeReducers\nimport { auto, mergeReducers } from 'redux-auto';\n...\n// pass into: reducers \u003e\u003e mergeReducers \u003e\u003e combineReducers\nconst store = createStore(combineReducers(mergeReducers(reducers)), middleware );\n...\n```\n\n### Using along side other Redux middleware. **Web-App*\n\n```JS\nimport logger from 'redux-logger';\nimport { auto, reducers } from 'redux-auto';\n...\n                    // pass all the middlewares in a normal arguments\nconst middleware = applyMiddleware( logger, auto(webpackModules, webpackModules.keys()))\nconst store = createStore(combineReducers(reducers), middleware );\n```\n\n### Using along side other Redux middleware. **React-Native*\n\n```JS\nimport logger from 'redux-logger';\nimport { auto, reducers } from 'redux-auto';\n...\n                    // pass all the middlewares in a normal arguments\nconst middleware = applyMiddleware( logger, auto(nativeStore))\nconst store = createStore(combineReducers(reducers), middleware );\n```\n\n### actions are available in the UI\n\nJust import \"redux-auto\" and the actions are automatically available by default\n\n```JS\nimport actions from 'redux-auto'\n...\n//action[folder][file]( data )\naction.apps.chageAppName({appId:123})\n```\n\n## action files\n\nThe action file lives within your attribute folder and becomes the exposed action.\nThe default export should be a function that will take 1) your piece of the state 2) the payload data\n\n\nExample: of an action to update the logged-in users name\n\n```JS\n// e.g. /store/user/changeUserName.js\nexport default function (user, payload) {\n  return Object.assign({}, user,{ name : payload.name } );\n}\n```\n\n★ Sometimes we want to talk to the server. This is done by action-middleware\n\nThis is done by exporting a function named \"action\" that returns a promise. The default function will now receive a 3rd argument \"state\". With the 2nd argument being the payload used to create the request\n\nExample: saving the uses name to the server\n\n```JS\n// /store/user/changeUserName.js\n\nexport default function (user, payload, stage, data) {\nswitch(stage){\n    case 'FULFILLED':\n     // ...\n      break;\n    case 'REJECTED':\n     // ...\n      break;\n    case 'PENDING':\n    default :\n     // ...\n      break;\n  }\n  return user;\n}\n\nexport function action (payload,user){\n\treturn fetch('/api/foo/bar/user/'+payload.userId)\n}\n```\n\nAn alternative declaration for the same as above\n\n```JS\n// /store/user/changeUserName.js\n\nexport function pending (posts, payload){\n  return posts\n}\n\nexport function fulfilled (posts, payload, serverPosts){\n  return serverPosts\n}\n\nexport function rejected (posts, payload, error){\n  return posts;\n}\n\nexport function action (payload,posts){\n\treturn fetch('/api/foo/bar/user/'+payload.userId)\n}\n```\n\n### chaining actions together\n\nYou chain actions back-to-back by setting an \"chain\" property on the exported function.\n\nAttach a function as the \"chain\" property\n\n**Example:**\n/store/user/getInfo\n```JS\nexport function fulfilled (user, payload, userFromServer){\n  return userFromServer;\n} fulfilled.chain = (user, payload, userFromServer) =\u003e actions.nav.move({page:\"home\"})\n\nexport function rejected (user, payload, userFromServer){\n  return userFromServer;\n} rejected.chain = actions.user.reset\n\nexport function pending (user, payload){\n  return user\n}\n\nexport function action (payload){\n\treturn fetch('/api/foo/bar/user/'+payload.userId)\n}\n```\n\nIf you pass your own function. Like with the **'fulfilled'** example. It will be passed all the arguments, the same as the host function was.\n\nElse you can pass **thought** an \"redux-auto\" action function. Like with the **'rejected'** example. It will called without any arguments.\n\nSo calling \"**actions.user.getInfo({userId:1})**\" will automatically call  **actions.nav.move** with the host arguments OR **actions.user.reset** *with out arguments.\n\n#### chaining to dispatcher\n\nChained functions can call the dispatcher directly.To trigger the dispatcher from your chain you need to return an `object` with a `type` and `payload`\n\n**Example:**\n```JS\nimport { push, replace } from 'react-router-redux';\n\nexport default function highLightFirend(friendID, {id}) {\n  return id;\n}\n\n// This will call the 3rd party \"router\" reducer\n\nhighLightFirend.chain = (friendID, {id})=\u003e{\n\n  const searchParams = new URLSearchParams(window.location.search);\n\n  if (!id) {\n    searchParams.delete(\"friend\");\n    const url = window.location.pathname+\"#\"+searchParams.toString()\n    return replace(url) // { type: '@@router/LOCATION_CHANGE',  payload: { ... } }\n\n  }else{\n    searchParams.set(\"resource\", id);\n    const url = window.location.pathname+\"#\"+searchParams.toString()\n   return push(url) // { type: '@@router/LOCATION_CHANGE',  payload: { ... } }\n  }\n\n}\n```\n\n### cancel an action\n\nYou can cancel an action from with-in the action .js file before it starts by not returning any value\n\n**Example:**\n```JS\nexport function action (payload,user){\n  if(payload.id === user.id)\n    return\n  else\n\t return fetch('/api/foo/bar/user/'+payload.userId)\n}\n```\n\n## index files\n\n**\"index\"** files are need for each attribute folder you make.\n\nThis file can exposes three funtions\n1) before\n2) default\n3) after\n\nYou can also [istening for other actions](#istening-for-other-actions) from other parts of the store.\n\n#### before\n\nFires on every action, to tweek the **payload** that will be passed to you logic functions.\n\n```js\n// add a time stamp to the payload that will be recived by user reduced\nexport function before(user, action){\n\n\treturn Object.assign({},action.payload,{ timeStamp : new Date() })\n}\n```\n\n#### default\n\nThis is a normal redux reducer function, being passed the **previousState** and the **action**.\n```js\nexport default function user(user = {name:\"?\"}, action) {\n  return user;\n}\n```\n\n⚠ This function will be fired on all actions, **EXCEPT** for actions that are handled by a specific action file in this reducer folder.\n\nLets understand this with an example:\n\n**Files:**\n```\nstore/\n ├──user/\n │  └── index.js\n │  └── changeName.js\n └──posts/\n    └── index.js\n    └── delete.js\n```\n**code:**\n\n```JS\nimport actions from 'redux-auto'\n...\nactions.user.changeName({name:\"brian\"})\n```\nThe default functions for **store/user/changeName.js** \u0026 **store/post/index.js** will be fired.\n\n**store/user/index.js was NOT** called because there is a specific action file a to handle it for user.\n\n\n#### after\n\nFires after every action, allowing you to change your piece of the **state**.\n\n```js\nimport actions from 'redux-auto'\n\n// automatically keep a log of all actions against user\nexport function after(newUserValues, action, oldUserValues){\n\n\tconst changes = {}\n\n\tif(action.type in actions.user) // log if this is a user action!\n\t\tchanges.log = newUserValues.concat(log,[{action.type:action.payload}])\n\n\treturn Object.assign({}, newUserValues, changes)\n}\n```\n\n#### listening for other actions\n\nThere are two built-in ways to detect other actions from within your index.\n1)You can find if the current fire action that you have received matches a specific action **and** 2) You can find if their current action is part of another piece of the store.\n\n1. To find if the correct action is a specific action. [Import the actions](#actions-are-available-in-the-ui) as you normally would and do a **loose** equality check.\n\n**Example:** We want to have a count of how many post our user has done\n```js\nimport actions from 'redux-auto'\n\nexport default function user(user = {name:\"?\", posts:0}, action) {\n\n  // You can check on each state of an asynchronous action\n  if(actions.posts.save.fulfilled == action.type){\n    return Object.assign({},user,{posts:user.posts+1})\n\n    // And non-synchronous actions can be checked directly\n  } else if(actions.posts.something == action.type){\n    // ... do some work ...\n  }\n\n  return user;\n}\n```\n\n2. If you wish to listen to all actions from a specific part of the store. You can use the `in` keyword.\n\n**Example:** We wish to log all post actions\n```js\nimport actions from 'redux-auto'\n\nexport default function logging(log = [], action) {\n\n  // test if the action type is within the posts\n  if(action.type in actions.posts){\n    return [...log, action]\n  }\n\n  return log;\n}\n```\n\n## handling async actions in your UI\n\nredux-auto has a built in mechanism to flag what stage an async action is in..\n\nif the state that you returned from your reduce function is an object or array. redux-auto will transparently attach a \"loading\" property representing all async actions.\n\nThe \"loading\" flag can have 1 of 4 values\n\n1) `undefined` : the async action has not been fired yet\n2) `true` : the action is in progress\n3) `false` : the action has completed successfully\n4) `error` : an error occurred and here is the error object + a \"clear\" function to reset the async to `undefined`\n\n* Note: The async action will also have the clear function if at any time you want to reset the \"loading\" property.\n\n     `actions.user.save()` is the async function and\n\n     `actions.user.save.clear()` will clear the \"loading\" property.\n\n\nexample:\n\n\n```JS\n\n// user = { name:\"tom\" }\n\nJSON.stringify(state.user) // \"{ \"name\":\"tom\" }\"\n\nstate.user.loading.save // = undefined\n\nactions.user.save()\n\nstate.user.loading.save // = true\n\n// when the request or promuse completed\n\nstate.user.loading.save // = false\n\n// if the was a problem. it will be was to the error object\n\nstate.user.loading.save // = Error(\"some problem\")\n// + with an Error, there will also be a \"clear\" function to set the \"loading\" back to undefined\n// e.g. state.user.loading.save.clear()\n\n```\n\u003e [handling async actions in your ui:- example](https://github.com/codemeasandwich/redux-auto/blob/master/example/ui/index.jsx)\n\n## smart actions\n\n**smart actions** is an options flag that handly `actions` function more intelligently.\n\nCurrently facilitates [graphql](http://graphql.org/) and [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) responses returned by action's promises.\n\n**To enable:**\n\n```JS\n\nimport { auto } from 'redux-auto';\n\nauto.settings({smartActions:true})\n\n```\n\nThis will now parce fetch and graphQL errors into your `rejected` function.\nAs well as parsing the json if available\n\n## Testing\n\nIf you want to use a testing frameworking. There is helper funcsion [/test/fsModules](https://github.com/codemeasandwich/redux-auto/blob/master/test/fsModules.js)\n\n**For [jest](https://jestjs.io/) example:**\n```JS\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport { createStore, applyMiddleware, combineReducers } from 'redux';\nimport { auto, reducers } from 'redux-auto';\nimport fsModules from 'redux-auto/test/fsModules'\nimport App from './Main';\nimport path from 'path';\nimport fs from 'fs';\n\nconst storePath = path.join(path.dirname(fs.realpathSync(__filename)), 'store');\nconst webpackModules = fsModules(storePath)\nconst middleware = applyMiddleware( auto(webpackModules, webpackModules.keys()))\nconst store = createStore(combineReducers(reducers), middleware );\n\nit('renders without crashing', () =\u003e {\n  const div = document.createElement('div');\n  ReactDOM.render(\u003cApp store={store} /\u003e, div);\n  ReactDOM.unmountComponentAtNode(div);\n});\n```\n\n\n## License\n\n[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fcodemeasandwich%2Fredux-auto.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fcodemeasandwich%2Fredux-auto?ref=badge_large)\n\n## Resources\n* [Presentation](https://gitpitch.com/codemeasandwich/redux-auto)\n\n[lifecycle]:https://s3-eu-west-1.amazonaws.com/redux-auto/flow.png\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodemeasandwich%2Fredux-auto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodemeasandwich%2Fredux-auto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodemeasandwich%2Fredux-auto/lists"}