{"id":16611550,"url":"https://github.com/valerybugakov/redux-boost","last_synced_at":"2025-10-29T18:30:28.853Z","repository":{"id":57132038,"uuid":"142284120","full_name":"valerybugakov/redux-boost","owner":"valerybugakov","description":"Redux-Boost makes it easy to make API calls and use them inside of the Redux store and React-components.","archived":false,"fork":false,"pushed_at":"2018-11-04T13:06:48.000Z","size":483,"stargazers_count":3,"open_issues_count":1,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-02T03:51:14.448Z","etag":null,"topics":["react-redux","redux","redux-saga","reselect"],"latest_commit_sha":null,"homepage":"https://codesandbox.io/s/3rqv2q3695","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/valerybugakov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-07-25T10:20:22.000Z","updated_at":"2019-10-29T10:18:32.000Z","dependencies_parsed_at":"2022-09-03T04:42:06.499Z","dependency_job_id":null,"html_url":"https://github.com/valerybugakov/redux-boost","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valerybugakov%2Fredux-boost","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valerybugakov%2Fredux-boost/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valerybugakov%2Fredux-boost/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valerybugakov%2Fredux-boost/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/valerybugakov","download_url":"https://codeload.github.com/valerybugakov/redux-boost/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238864175,"owners_count":19543487,"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":["react-redux","redux","redux-saga","reselect"],"created_at":"2024-10-12T01:38:18.095Z","updated_at":"2025-10-29T18:30:23.458Z","avatar_url":"https://github.com/valerybugakov.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Redux Boost\n\n`redux-boost` works with [React Redux](https://github.com/reactjs/react-redux) to enable smooth HTTP request handling in [React](https://github.com/facebook/react) using [Redux](https://github.com/reactjs/redux) to store all the request updates.\n\n## TODO:\n\n- [x] Add [interactive example app](https://codesandbox.io/s/3rqv2q3695)\n- [x] Add Getting Started section\n- [ ] Add API reference (WIP)\n\n## Installation\n\n`npm install --save redux-boost`\n\nor\n\n`yarn add redux-boost`\n\n\n## Documentation\n\n* [**Getting Started**](#getting-started)\n    - [Overview](#overview)\n    - [Data flow](#data-flow)\n    - [Basic Usage Guide](#basic-usage-guide)\n* [**API**](#api)\n  - [createRequestMiddleware()](#createrequestmiddlewareconfig)\n  - [createActions()](#createactionsactiontypes--prefix)\n  - [createReducer()](#createreducerhandlers--defaultstate)\n  - [restQuery()](#)\n  - [restMutation()](#)\n  - [createSaga()](#)\n  - [selectors](#)\n* [**Examples**](#examples)\n    + [Store configuration](#store-configuration)\n    + [React integration](#react-integration)\n\n\n## Getting Started\n\nThe basic implementation of `redux-boost` is simple. However, to make the most of it, it's recommended to have basic knowledge on:\n\n- [Redux](http://redux.js.org/) state container,\n- [React](https://facebook.github.io/react/) and [Higher-Order Components (HOCs)](https://facebook.github.io/react/docs/higher-order-components.html).\n\n### Overview\n\nTo connect your React form components to your Redux store you'll need the following pieces from the `redux-boost` package:\n\n- Redux middleware `createRequestMiddleware()`.\n- Redux Reducers: `requestsReducer` and `dataReducer`,\n- React HOC `restQuery()` for `GET` requests.\n- React HOC `restMutation()` for other types of requests.\n\nIt's important to understand their responsibilities:\n\n|               | type        | responsibility                                                                                                                                                   |\n| ------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `requestsReducer` `dataReducer` | _reducer_   | function that tells how to update the Redux store based on changes coming from the application; those changes are described by Redux actions                     |\n| `restQuery()` `restMutation()` | _HOC_       | function that takes configuration object and returns a new function; use it to wrap your React component and bind user interaction to dispatch of Redux actions |\n| `requestMiddleware` | _middleware_       | function that provides a third-party extension point between dispatching an action, and the moment it reaches the reducer |\n\n\n### Data flow\n\nThe diagram below represents the simplified data flow. Note that in most cases you don't need to worry about the [action creators](http://redux-form.com/7.4.2/docs/api/ActionCreators.md/) for yourself, as they're already bound to dispatch for certain actions.\n\nLet's go through a simple example. We have a React component wrapped with `restQuery()`. There is a list inside of it that need data to be fetched from the server to be rendered correctly. The data flows like this:\n\n1.  Component gets rendered for the first time,\n2.  `restQuery` dispatches `fetchStart` action with `query` description passed into it,\n3.  `requestMiddleware` catches this action and initiates HTTP request,\n4.  `requestsReducer` catches this action too and updatse Redux state with request state,\n5.  `restQuery` updates wrapped component passing request state into it,\n6.  On request resolution `requestMiddleware` dispatches a relevant action, either `fetchSuccess` or `fetchFail`,\n7.  `requestsReducer` updates request state with the result or an error\n8.  `restQuery` updates wrapped component with the final request state,\n\nWith `redux-boost` comes a lot more: hooks for error handling and props to refetch the data, data selectors and action creators.\nThis guide describes the basic usage – feel free to dig deeper.\n\n### Basic Usage Guide\n\n#### Step 1 of 3: Request middleware\n\nThe store should know how to handle actions with quert descriptions to intiate HTTP requests. To enable this, we need to pass the `createRequestMiddleware` to your store.\n\n```js\nimport axios from 'axios'\nimport { applyMiddleware } from 'redux'\nimport { createRequestMiddleware } from 'redux-boost'\n\nconst middlewares = [\n  createRequestMiddleware({\n    // Executor is function that will initiate HTTP requests\n    executor: axios\n }),\n  // ...otherMiddlewares,\n]\n\nexport const enhancer = applyMiddleware(...middlewares)\n```\n\nNow your store knows how to initiate HTTP requests received from the certain actions.\n\n#### Step 2 of 3: Request and data reducers\n\nThe store should know how to handle actions coming from the form components. To enable this, we need to pass the `requestsReducer` and `dataReducer` to your store. It serves for **all of your form components**, so you only have to pass it once.\n\n```js\nimport { createStore, combineReducers } from 'redux'\nimport { dataReducer, requestsReducer } from 'redux-boost'\n\nconst rootReducer = combineReducers({\n  // ...your other reducers here\n  // you have to pass requestsReducer under 'requests' key,\n  // and dataReducer under 'data` key.\n  data: dataReducer,\n  requests: requestsReducer,\n})\n\nconst store = createStore(rootReducer)\n```\n\nNow your store knows how to handle actions coming from the form components.\n\n**NOTE:** The keys used to pass the `redux-boost` reducer should be named **`requests`** and **`data`**.\n\n#### Step 3 of 3: High-order components\n\nTo make your React component communicate with the store, we need to wrap it with `restQuery()` to receive data from the server or `restMutation()` to mutate data on the server. It will provide the props about the current request state and function to refetch data.\n\n```js\nimport React from 'react'\nimport { restQuery } from 'redux-boost'\n\nconst FriendList = props =\u003e {\n  // getFriends prop updates on each request stage: [START, SUCCESS, FAIL]\n  const { getFriends: { result, loading, error } } = props\n\n  if (loading) return \u003cSpinner\u003e...loading\u003c/Spinner\u003e\n  if (error) return \u003cError\u003e{error.message}\u003c/Error\u003e\n\n  return result.map(friend =\u003e \u003cdiv key={friend.id}\u003e{friend.name}\u003c/div\u003e)\n}\n\nexport default restQuery({\n  // a unique name the request, will be used as a prop name and redux state key\n  name: 'getFriends'\n\n  // options allows to use ownProps of the component\n  // to create dynamic parts of the query\n  options: ({ userId }) =\u003e ({\n    // payload will be passed to the executor function, it's axios in our example\n    payload: 'http://awesome.api.com/friends?userId=${userId}'\n  }),\n\n  // we don't want to fetch anything if userId is not defined\n  skip: ({ userId }) =\u003e !userId,\n})(FriendList)\n```\n\n**NOTE**: If the `()()` syntax seems confusing, you can always break it down\ninto two steps:\n\n```js\n// ...\n\n// create new, \"configured\" function\ncreateRestQuery = restQuery(configuration)\n\n// evaluate it for FriendList component\nconst FriendListContainer = createRestQuery(FriendList)\n\nexport default FriendListContainer\n```\n## API\n\n#### `createRequestMiddleware(config)`\n\nCreates a middleware that will handle request actions generated by the library to make network requests. Configuration keys:\n\n\n| Key         | Default value     | Description                                                                                                                                                                                                                                                                                                             |\n| ----------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| method    | `'GET'`  | Default HTTP method to use if no method passed in the request action.                                                                                                                                                                                                                               |                                                                                                                                               |\n| executor       | `fetch()`   | A function that will execute HTTP request `executor(...requestAction.payload)` |\n| prepareExecutor    |    | A function that is called before the request to alter executor in some way using request description. `prepareExecutor(executor, requestAction)`           \n| serialize      |   | A function that is called with the body of the response, allowing you to transform it. `serialize(response)`                                                                                         |\n| onSuccess        |   | A function that is only called on network request successful completion. `onSuccess({ name, result })`                                                                                                                                                                                       |\n| onError     |  | A function that is only called on network request error. `onError({ name, error })`                                                                                                                                                                                                                            |\n\n\n#### `createActions(actionTypes [, prefix])`\n\nReturns an object with [action creators](#action-creator). If `prefix` is passed, it will be used to prefix type in each action creator.\n\n```js\nconst actions = createActions([\n  'set',\n  'delete',\n  'update',\n], 'users')\n\n// actions.set(profile)         -\u003e { type: 'users/set', payload: profile }\n// actions.delete({ id: 77 })   -\u003e { type: 'users/delete', payload: { id: 77 } }\n\n// actions.set.type             === 'users/set'\n// String(actions.set)          === 'users/set'\n// actions.set.isReduxAction    === true\n```\nApart from `payload`, the second argument in action creator creates `meta` property, which can be useful in middlewares.\n```js\n// actions.update({ name }, callback) -\u003e \n// {\n//   type: 'users/update',\n//   payload: { name },\n//   meta: callback\n// }\n```\n\n#### `createReducer(handlers [, defaultState])`\n\nReturns a new reducer.\n\n- **handlers**: `object`, that provides mapping between action types and action handlers.\n- **defaultState** (anything, optional): the initial state of the reducer. Must not be empty if you plan to use this reducer inside a `combineReducers`\n\n```javascript\nimport { actions } from './actions'\n\nconst DECREMENT_ACTION_TYPE = 'DECREMENT'\n\nconst counterReducer = createReducer({\n  [actions.increment]: (state) =\u003e state + 1,\n  [actions.add]: (state, payload) =\u003e state + payload,\n  [DECREMENT_ACTION_TYPE]: state =\u003e state - 1,\n}, 0)\n```\n\n## Examples\n\n### Store configuration\n\n```js\n/* eslint-disable */\nimport { combineReducers } from 'redux'\nimport {\n  boostStore,\n  createStore,\n  dataReducer,\n  requestsReducer,\n} from 'redux-boost'\n\nconst myReducer = combineReducers({\n  data: dataReducer,\n  requests: requestsReducer,\n})\n\nconst { store, persistor } = createStore({\n  initialState: {},\n  reducer: myReducer,\n\n  reduxLogger: 'fallback',\n  devtoolExtension: true,\n\n  // --- Redux persist config\n  reduxPersist: {\n    key: 'root',\n    whitelist: ['auth'],\n    // storage,\n  },\n\n  // --- Middlewares config\n  middlewares: {\n    saga: true,\n    thunk: true,\n    eventFilter: true,\n  },\n\n  // middlewares: [\n  //   ...customMiddlewaresArray,\n  // ],\n})\n\n// If store is not created by redux-boost\n// `boostStore` call is required to enable binded `fetchStart` action\nboostStore(store)\n```\n\n### React integration\n\n`restQuery` — HOC for `GET` requests.\n\n`restMutation` – HOC for other types of requests.\n\nAPI is similar to [React-Apollo](https://github.com/apollographql/react-apollo) library.\n\nFirst wrap React component into HOC to receive desired functionality.\n\n```js\nimport { compose } from 'redux'\nimport { connect } from 'react-redux'\nimport { getResponse, restMutation, restQuery } from 'redux-boost'\n\nimport exampleSaga from 'modules/sagas/exampleSaga'\n\nimport ExampleComponent from './ExampleComponent'\n\nconst mapStateToProps = state =\u003e ({\n  latestQuote: getResponse(state, { operation: 'operationName' }),\n})\n\nexport default compose(\n  connect(mapStateToProps),\n\n  // On componentDidMount will execute declared action which should\n  // return promise to track request status and receive result later in props\n  restQuery({\n    name: 'requiredData',\n    action: fetchRequiredData,\n    options: props =\u003e ({\n      userId: props.userId,\n    }),\n  }),\n\n  // Provides props to make request, track it's status and receive result\n  restMutation({\n    // {\n    //   name: 'exampleSaga',\n    //   action: exampleSagaAction,\n    // }\n    ...exampleSaga,\n\n    // Will be passed to the executor as a request params\n    options: ({ foo, bar }) =\u003e ({\n      foo: foo.toUpperCase(),\n      bar,\n      constant: 1,\n    }),\n  })\n)(ExampleComponent)\n```\n\nThen use provided props to send mutation request and display queried data.\n\n```js\n// In React component use mutation name to access passed properties\nimport React, { Component } from 'react'\n\nclass ExampleComponent extends Component {\n  executeMutation = async () =\u003e {\n    // Mutation result promise which resolves with result of the mutation\n    // But anyway mutation result will be passed as a prop to the component too\n    const result = await this.props.exampleSaga.mutate({\n      additionalProp: 'value', // Will be merged into the params defined in container\n    })\n\n    console.log(result)\n  }\n\n  render() {\n    const {\n      requiredData,\n      exampleSaga: { loading, mutate, result, error },\n    } = this.props\n\n    if (requiredData.loading) {\n      return \u003cdiv\u003e...loading\u003c/div\u003e\n    }\n\n    return (\n      \u003cdiv\u003e\n        \u003cData {...requiredData.result} /\u003e\n        \u003cbutton onClick={this.executeMutation}\u003eMutate\u003c/button\u003e\n\n        {loading \u0026\u0026 '...mutation result is loading'}\n        {result \u0026\u0026 \u003cResultComponent {...result} /\u003e}\n      \u003c/div\u003e\n    )\n  }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvalerybugakov%2Fredux-boost","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvalerybugakov%2Fredux-boost","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvalerybugakov%2Fredux-boost/lists"}