Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/anish000kumar/redux-box
Modular and easy-to-grasp redux based state management, with least boilerplate
https://github.com/anish000kumar/redux-box
react redux redux-saga
Last synced: 7 days ago
JSON representation
Modular and easy-to-grasp redux based state management, with least boilerplate
- Host: GitHub
- URL: https://github.com/anish000kumar/redux-box
- Owner: anish000kumar
- License: mit
- Created: 2018-01-08T12:54:24.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2023-11-28T05:18:48.000Z (about 1 year ago)
- Last Synced: 2024-05-21T06:54:47.786Z (8 months ago)
- Topics: react, redux, redux-saga
- Language: JavaScript
- Homepage:
- Size: 6.12 MB
- Stars: 710
- Watchers: 20
- Forks: 25
- Open Issues: 13
-
Metadata Files:
- Readme: readme.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
- awesome-react-render-props - redux-box - to-grasp redux based state management, with least boilerplate (Components / Data)
- awesome-list - redux-box - to-grasp redux based state management, with least boilerplate | anish000kumar | 719 | (JavaScript)
- awesome-react-render-props - redux-box - to-grasp redux based state management, with least boilerplate (Components / Data)
- awesome-react-state-management - redux-box - Modular and easy-to-grasp redux based state management, with least boilerplate (List)
- awesome-f2e-libs - **redux-box**
README
# Redux Box 2
[![Build Status](https://travis-ci.org/anish000kumar/redux-box.svg?branch=master)](https://travis-ci.org/anish000kumar/redux-box)
![Coveralls github](https://img.shields.io/coveralls/github/anish000kumar/redux-box)
![GitHub](https://img.shields.io/github/license/anish000kumar/redux-box)
![GitHub last commit](https://img.shields.io/github/last-commit/anish000kumar/redux-box)**Note: If migrating from version 1.x.x to 2, you would find breaking changes.**
Redux-Box aims at abstracting away the complexity in using redux with redux-saga, while letting you manage application state in modular fashion, without losing the flexibility or without introducing new bizarre terms.
It organizes your store as collection of **independent** modules which can be used across different stores/applications/platforms.Illustration by Vikas
## Table of contents:
- [Redux Box 2](#redux-box-2)
- [Table of contents:](#table-of-contents)
- [What's it for:](#whats-it-for)
- [Installation](#installation)
- [Note for React Native:](#note-for-react-native)
- [The Basics](#the-basics)
- [Usage](#usage)
- [step 1: create a module](#step-1-create-a-module)
- [step 2 : register the module in redux store](#step-2--register-the-module-in-redux-store)
- [step 3: Use the module in component](#step-3-use-the-module-in-component)
- [through decorator](#through-decorator)
- [Live Examples (For [email protected] only)](#live-examples-for-reduxbox1xx-only)
- [Upcoming Features](#upcoming-features)
- [FAQs](#faqs)## What's it for:
1. **Clean, expressive and minimal reducers:**
If you prefer keeping your code expressive, you will feel right at home with redux-box. Have a look at a simple reducer written with and without redux-box:If you are concerned about the state getting mutated directly in the snippet above, then you need not be. Because the `state` being passed to a mutation is **NOT** the actual `state object` of application, instead it's a [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) of the state. Redux-box relies on wonderful [immer](https://github.com/mweststrate/immer) library to achieve the expressiveness you see above.
2. **Organise your giant state into modules**
3. **Setup redux+redux-saga for our react/react-native app in a trice**
4. **Simplified Sagas**
5. **Just import and use store**:
You wouldn't need to write a dedicated HOC to interact with your store. If you need to interact with a particular store-module, you can simply import it and use it. As simple as that! Redux box offers two ways of using a module in your component : using `@connectStore` decorator or using `render props`. (refer to the usage section for better reference)## Installation
```
npm install --save redux-box
```OR
```
yarn add redux-box
```#### Note for React Native:
To support the latest decorator and generator syntax, you would want to use the `.babelrc` file as below:
```
{
"presets": [
"babel-preset-react-native-stage-0/decorator-support"
],
"env": {
"development": {
"plugins": [
"transform-react-jsx-source",
"transform-es2015-typeof-symbol"
]
},
"production": {
"plugins": ["transform-remove-console"]
}
}
}
```## The Basics
Redux box emphasizes on dividing the whole application into multiple modules. Each of these modules manages it's state seperately, with the help of 5 segments (You can skip the segments you don't need in your project):
1. state
(It specifies the initial state of the module)2. mutations
(It specifies the function to be run when a specific action is dispatched, it's same as reducer but clutter-free)3. dispatchers
(it contains the actionCreators for your store. Each method of this object must return an action object )4. sagas
(this is where you write all your sagas / async operations)5. selectors
( selectors can be thought of as getters or computed properties from your state)## Usage
### step 1: create a module
Make sure you specify a unique name for each module ('user' in this example)
```javascript
// store/user.js
import { createSagas, createContainer, createModule } from "redux-box";
import { call } from "redux-saga/effects";// define initial state
const state = {
name: "John",
email: "[email protected]",
todos: [{ name: "First", type: 1 }, { name: "Second", type: 0 }]
};// define dispatchers
export const dispatchers = {
setName: name => ({ type: "SET_NAME", name }),
setEmail: email => ({ type: "SET_EMAIL", email })
};// define mutations
const mutations = {
SET_NAME: (state, action) => (state.name = action.name),
SET_EMAIL: (state, action) => (state.email = action.email)
};// define sagas
const sagas = createSagas({
SET_EMAIL: function*(action) {
const response = yield call(api.updateEmail, action.email);
}
});// selectors
export const getTodos = (state) => state.todos
export const getCompletedTodos = createSelector( getTodos, (todos) => {
return todos.filter(todo => todo.type==1)
})
const selectors = {
getTodos,
getCompletedTodos
};export default createModule({
state,
mutations,
sagas,
});
```### step 2 : register the module in redux store
```javascript
import { createStore } from "redux-box";
import userModule from "./user";export default createStore({
user: userModule
});
```OPTIONAL: if you need to create store with some reducers and middlewares, the signature of createStore method from redux-box goes like this:(if you have already included a module in modules array, you need **NOT** to register it's sagas or reducers manually by including in config object)
```javascript
createStore((modules: Object), (config: Object));//example config object
config = {//define redux middlewares
middlewares: [],
//define the default state for your store
preloadedState: {},// sagas to be manually registered
sagas: [userModule.sagas, testModule.sagas],// reducers to be manually registered
reducers: {
user: yourCustomReducer
},
decorateReducer: reducer => {
//do something
return newReducer;
},
//overrite the compose function
composeRedux: (composer) => {
// do something
// return modified compose function
},
// Dynamically decide when to enable or disable dev-tools
enableDevTools: () => true,
devToolOptions: {}
};
```After this you would need to wrap your root component around the Provider tag like so :
```javascript
import React from "react";
import { Provider } from "react-redux";
import store from "./store";
import RootComponent from "./components/RootComponent";class App extends React.component {
render() {
return (
);
}
}export default App;
```### step 3: Use the module in component
#### through decorator
```javascript
import React, { Component } from "react";
import { connectStore } from "redux-box";
import { getTodos, getCompletedTodos, dispatchers } from "./user";@connectStore({
mapState: state => ({
user: state.user
}),mapSelectors: {
todos: getTodos,
completedTodos: getCompletedTodos
},mapDispatchers: {
setName: dispatchers.setName,
setEmail: dispatchers.setEmail
}
})
export default class AppComponent extends Component {
componentDidMount() {
console.log(this.props.user);
/*
{
name : 'John',
email : '[email protected]',
getTodos: [{ name: "First", type: 1 }, { name: "Second", type: 0 }],
getCompletedTodos: [{ name: "First", type: 1 }],
setName : fn(arg),
setEmail : fn(arg)
}
*/
}render() {
const { user } = this.props;
return (
{user.name}
{user.email}
{
this.props.setName('jane doe')
}}> Change Name
);
}
}
```## Live Examples (For [email protected] only)
**Docs for V2 are in progress, would be updated in a few days**Here are some examples to let you play around with redux-box
1. Basic example - https://stackblitz.com/edit/react-3c8vsn?file=Hello.js
2. Example showing redux-saga usage: - https://stackblitz.com/edit/react-qmedt4?file=Hello.js
3. Example usage with redux-form: https://stackblitz.com/edit/react-w4dqth?file=store%2Findex.js
4. Example usage with redux-persist : https://stackblitz.com/edit/react-pezrbb?file=store%2Findex.js
5. Example showing usage of preloaded state for SSR: https://stackblitz.com/edit/react-qcasn4?file=store/index.js
6. Using redux-observable: https://stackblitz.com/edit/react-zu8qjn?file=store%2Fuser%2Fepics.js## Upcoming Features
*No pending feature requests*
> *[Suggest a new feature here](https://github.com/anish000kumar/redux-box/labels/feature)*## FAQs
1. **Error:**
```
Either wrap the root component in a , or explicitly pass “store” as a prop to "Connect(MyComponent)
```
This error occurs because of mismatch among versions of dependencies of redux-box, most likely `react-redux`. You can run this command to fix this issue for now:
```
yarn add [email protected]
```
1. **Decorators aren't working**Decorators aren't still a part of es6. To use the decorator syntax you should be using a transpiler like babel. Also, in create-react-app projects the `.babelrc` file doesn't really work so you would need to run `npm run eject` to be able to use custom babel-plugins. Following `.babelrc` should suffice:
```javascript
{
"plugins": ["transform-decorators-legacy", "styled-components"],
"presets": [ "react","es2015", "stage-2" ]
}
```In case you wouldn't like to eject, you can still use redux-box without decorators. Like so:
```javascript
@connectStore({
ui: uiModule
})
class TestComponent extends React.Component{
...
}
export default TestComponent```
Above snippet is equivalent to:
```javascript
class TestComponent extends React.Component{
...
}export default connectStore({
ui: uiModule
})(TestComponent)```
2. **Can I use all the features of redux-box, with `createStore` from redux instead?**
Yes, you can! Here's the script showing how you can use `createStore` from redux, to setup your modules (with reducers, sagas and middlewares):
(v1.3.9 onwards)```javascript
import { applyMiddleware, combineReducers, compose, createStore } from "redux";
import createSagaMiddleware from "redux-saga";
import { all } from "redux-saga/effects";
import { moduleToReducer } from "redux-box";
import { module as homeModule } from "./home";
import { module as userModule } from "./user";//hook up your module reducers
const combinedReducer = combineReducers({
home: moduleToReducer(homeModule),
user: moduleToReducer(userModule)
});// hook up your module sagas
const sagas = [...homeModule.sagas, ...userModule.sagas];// hook up your middlewares here
const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware];//what follows below is the usual approach of setting up store
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
let enhancer = composeEnhancers(applyMiddleware(...middlewares));function* rootSaga() {
yield all(sagas);
}const store = createStore(combinedReducer, enhancer);
sagaMiddleware.run(rootSaga);
export default store;
```