preface

Ask the question, why redux? What problem does it solve?

The React website saysA JavaScript library for building user interfaces“, which means JavaScript libraries that focus on building user interfaces. Is used to bringstate / propsThe data,JSXConvert the code to a real HTML page. React, like vue.js, does not manage global state (data). React transfers data from parent to child components from top to bottom.



In the project, we often have such a requirement, according to the current login user information display different menus or data, this requires that we can obtain the login user information in any component, how to do? Without these frameworks, we would certainly find a way to store the global information in a unified place, design the corresponding data structure exists in a JS object, and this object mustThe singletonEnsure that the data obtained each time is the same. For modification logic, there are also established rules to be followed to ensure that every data modification is traceable. Without the conventions of these rules, state (data) changes become chaotic, code becomes chaotic, and potential problems with the system multiply.

Redux has emerged for managing state (data) in projects. And Redux tries to make state changes predictable. Let’s go down.

redux

The figure above shows the flow of REdux data. The React component or other components passstore.getState()StoreTo retrieve data. You can modify data only through thestore.dispatch(action)StoreSubmit instructions to modify data,StoreData modification is handled uniformly.ReducersIs the corresponding logical handler function that takes two arguments(previousState, action)The action directive, representing the value of the previous state state and the action directive, returns a new data newState. Pay attention toReducersIs a pure function that does not modify the received data directly, but returns a new data newState; At the same time, once(previousState, action)Sure, the result is the same multiple times. The whole process is redux’s three principles:

  • Single data source: Data in a store is singleton and globally unique.

  • State is read-only: the only way to change State is to trigger action, store.dispatch(action); Modifying state directly does not work.

  • Reducers are pure functions that ensure that the results of repeated implementation are the same under the premise of (previousState, Action) determination; Instead of directly modifying the state, the Store triggers the change of state.

In addition, redux has three important functions:

// Initialize the store
const store = createStore(reducers);

/ / for the state
store.getState();

// Trigger action to request data modification
store.dispatch(action);

// The subscription data changes callback function
store.subscribe(() = >{
   //to do
});
Copy the code

Redux is easy to use

Instead of using React or Vue, we’ll simply use redux as an example. Build a small project using Webpack and add NPM install redux -d to the project. The directory structure is as follows:

├─ ├─ SRC │ index.html │ ├─ ├─build │ ├.dev.config.js // ├─ SRC │ index.html │ // ├─build │ ├.dev.config.js // ├─ SRC │ index.html │ ├─ Actions │ actionCreator. Js // Action Creator; ├─ ├─ ├─ ├─ download.txt // download.txt // download.txt // download.txtCopy the code

In the index.js entry file, we initialize the store data, bind the button to events, and send the action to the store via store.dispatch(addCounter(1)).

//index.js
import store from './store';
import {addCounter} from './actions/actionCreator';

/ / initialization
const stateData = store.getState();
document.getElementById("counter").innerHTML = stateData.counter;
document.getElementById("otherUsed").innerHTML = stateData.counter;
document.getElementById("myBtn").addEventListener("click".() = >{
    / / triggers
    store.dispatch(addCounter(1));
})

/ / subscribe
store.subscribe(() = >{
    const stateData = store.getState();
    document.getElementById("counter").innerHTML = stateData.counter;
    document.getElementById("otherUsed").innerHTML = stateData.counter;
})
Copy the code

The task is to process the data logic according to the type of the action:

// reducers/index.js
const defaultState = {
    counter: 10
}
export default (state = defaultState, action)=>{
    if(action.type==="add") {const newState = JSON.parse(JSON.stringify(state));
        newState.counter = newState.counter + action.value;
        return newState;
    }
    return state;
}
Copy the code

This example shows that Redux and React are not strongly coupled and can be used independently. Source code address: github.com/YY88Xu/redu…

React + Redux (todoList)

Create react-app initializes the project with the following directory structure:

Todo-list │ package.json │ package.json │ ├─ Public │ Favicon. Ico │ index.html │ logo192 Logo512. PNG │ │ manifest. Json │ │ robots. TXT │ │ │ └ ─ API │ initList. Json │ └ ─ SRC │ App. CSS │ App. Js │ App. Test, js │ │ ├─ Common │ Actiontypes.js │ ├─ Reacts-redux // Reacts-redux │ ├─ Actions │ actionCreator. Js │ │ ├─ Components │ │ Todolist.js │ │ ├─reducers │ index.js │ │ ├─ Reacts-Redux │ ├─ Actions │ actionCreator └ ─ store │ index. Js │ ├ ─ redux / / redux │ ├ ─ actions │ │ actionCreator. Js │ │ │ ├ ─ components │ │ TodoList. Js │ │ │ ├ ─ reducers │ │ index. Js │ │ │ └ ─ store │ index. The js │ ├ ─ redux - saga / / story - saga implementation │ ├ ─ actions │ │ actionCreator. Js │ │ │ ├ ─ components │ │ TodoList. Js │ │ │ ├ ─ reducers │ │ index. The js │ │ │ ├ ─ saga │ │ index. The js │ │ │ └ ─ store │ index. The js │ ├─ ├─ ├─ exercises, exercises, exercises, exercises, exercises, exercises, exercises, exercises, exercises, exercises └ ─ store index. JsCopy the code
actions

A function that returns an action object, usually with a type attribute. Responsible for the generation of instructions, the page through store.dispatch(action) to send data modification requests to the store.

reducers

A pure function that takes two arguments (previousState, action), the first of which represents the value of the previousState. Action is the action passed from the previous page to the store via store.dispatch(action). Reducers handles the action’s type value differently, returning a new variable, newState. Reducers cannot directly modify the previousState passed in.

store

Pass const store = createStore(Reducer); Create a Store object.

To import the React component file, run this.state = store.getState(); Let the component get the data in the store;

And subscribe to changes in the store data in the componentDidMount lifecycle function:

componentDidMount(){
    store.subscribe(() = >{
        this.setState(store.getState());
    });
}
Copy the code

react-redux

Redux redux redux redux redux redux redux redux

npm install react-redux -D
Copy the code
  • <Provider>Component, which simplifies the process of importing the React component file store each time.
import { Provider } from "react-redux";
<Provider store={store}>/ / store inside<TodoList/>
</Provider>
Copy the code
  • mapStateToPropsFunction to storestatecomponent-to-componentprops
  • mapDispatchToPropsFunction, passed indispatchUsed to trigger an action.
  • connectFunction.
const mapStateToProps = (state) = >({
    inputValue: state.inputValue,
    list: state.list
})

const mapDispatchToProps = (dispatch) = > ({
    hanldeAdd: () = >{
        dispatch(addItem());
    },
    changeInputValue:  (e) = >{
        dispatch(changeValue(e.target.value));
    },
    deleteItem: (key) = >{
        dispatch(deleteItem(key))
    }
})
MapStateToProps and mapDispatchToProps
// And returns a function that accepts TodoList to return a wrapped component.
export default connect(mapStateToProps, mapDispatchToProps)(TodoList);
Copy the code

asynchronous

Two middleware that handle asynchronous Redux, Redux-Thunk and Redux-Saga.

Or a question? What does middleware mean between where and where? The answer is: “play some tricks” between Actions and reducers, processing asynchronous requests before triggering dispatch to initiate operations to the Store.

Without this middleware, we could have implemented the business requirements. Using these middleware is a way to strip asynchronous logic away, with cleaner code and higher maintainability.

redux-thunk

Use with Redux enhances redux’s capabilities. Before actions return an object, asynchronous actions can return a function:

// actionCreator.js
export const getInitList = () = > {
    return function(dispatch){
        axios.get("/api/initList.json").then(res= >{
            dispatch(initList(res.data))
        })
    }
}
Copy the code

Redux-thunk creates a store

import { createStore, applyMiddleware } from "redux";
import thunk from 'redux-thunk';
import reducer from '.. /reducers';

const store = createStore(reducer, applyMiddleware(thunk));

export default store;
Copy the code

See the code in the project’s redux-Thunk folder for details.

redux-saga

Redux-saga is also a middleware component of Redux that handles asynchronous actions. TakeEvery (GET_INIT_LIST, getInitList) to listen for action.type is the GET_INIT_LIST action that executes the getInitList method to get asynchronous data.

// saga/index.js
import {takeEvery, put} from 'redux-saga/effects';
import { GET_INIT_LIST } from ".. /.. /common/actionTypes.js";
import { initList} from ".. /actions/actionCreator.js";
import axios from 'axios';
function* getInitList(){
    try{
        const res = yield axios.get("/api/initList.json");
        const action = initList(res.data);
        yield put(action);
    }catch(e){
        console.log("Initlist. json network request failed")}}function* mySaga(){
    yield takeEvery(GET_INIT_LIST, getInitList);
}
export default mySaga;
Copy the code

See the code in the project’s Redux-Saga folder for details.

The source code

Address: github.com/YY88Xu/todo…

The last

If there is any mistake or not precise place, please give correction, thank you very much. If you like or have some inspiration, welcome to like, to the author is also a kind of encouragement.