• Source code github repository (state-management-compare) master master branch horizontal comparison of front-end development in different state management schemes
  • The original article appears in Readux 202008
  • Update record
    • Updated on 22 August 2020
    • First published on 16 April 2020

[TOC]

directory

  1. introduce

  2. counter

introduce

An overview of the

Redux is a predictable state container for JavaScript applications (size 2KB)

Installation tools

yarn add @reduxjs/toolkit
yarn add redux
npm install --save-dev redux-devtools
Copy the code

A simple case

// action type
const ADD = "ADD";
// action creator
const counterActionCreator = () = > ({
  type: ADD,
  payload: {},});// init State
const initState = {
  number: 0.list: [{id: "1"],},info: {
    userName: "yayxs",}};// A reducer
const counterReducer = (initState, counterActionCreator()) = > {};
// create store
let store = createStore(counterReducer);
// subscribe
store.subscribe(() = > console.log(store.getState()));
//change state
store.dispatch(counterActionCreator());
Copy the code

Action

An action is an action that moves data from an application to the warehouse, the data warehouse

  • JS object

  • format

    {
        type:ADD, // The type field is the convention, which means the name of the action,
        index:1.content:'This is a little bit of content.'
    }
    Copy the code
    • The object’stypeThe field represents the action performed; String constant
    • Index A unique ID, mainly an identifier
    • A content (or other) JS object can have any field, just one field

In real development we are used to creating the action function

const addAction = (params) = > {
  return {
    type: ADD, ... params, }; };Copy the code

Reducer

Now we still don’t talk about store, now we have action, but action it just describes the action, but it doesn’t know how to update the data, and when it comes to data, let’s say

{
  num: 0;
}
Copy the code

This simple JS object is data

ACTION is a normal object; REDUCER is a normal function

  • Say ordinary also not ordinary, js function only

    • function(a,b){
          console.log(a+b)
      }
      Copy the code
    • But it’s not that simple

  • Clean and simple,

  • // Default data
    const initData = {
      num: 123};// reducer
    const counterReducer = (state = initData, action) = > {
      // Do nothing, return the state passed in (initData by default)
      return state;
    };
    Copy the code
  • How is it possible to do nothing

import { addAction } from "./actions";
// Default data
const initData = {
  num: 123
};
// reducer
const counterReducer = (state = initData, action) = > {
  // What type of action is passed in
  switch (action.type) {
    case addAction:
      return Object.assign({}, state, {
        ...
      });
    default:
      return state;
  }

  // Do nothing, return the state passed in (initData by default)
  // return state;
};


Copy the code

Pay attention to

  • You cannot modify incoming data
  • By default, you must return the old onestate

Store

  • This is the state store, maintaining the state
  • The getState() method gets the state
  • Provide the dispatch () method to send the action
  • Subscribe () to register listening

To obtain state

getState();
Copy the code

Update the status

dispatch(action);
Copy the code

That’s what we call handing out an action

Register to listen (subscribe)

subscribe(listener);
Copy the code

The three principles

  • ** Single source of truth**

    • {
        "number": 123."obj": {},
        "list": []}Copy the code
  • State is read-only (** state is read-only**)

  • ** Changes are made with pure functions**

Part of the

  • state
    • Server data
    • UI data
    • app state
  • Action
  • Reducer
  • Store

counter

At this point, the question is, with all this, how do we create this repository

yarn add redux
Copy the code

There’s a method in this library, which we call redux

Build the action

import { ADD_TYPE } from "./actionTypes";
const actionCreator = (params) = > ({
  type: ADD_TYPE,
  payload: {
    ...params,
  },
});

export { addAction };
Copy the code

Build the reducer

import { actionCreator } from "./actions";
// Default data
const InitialState = {
  info: {
    counter: 0,}};// reducer
const counterReducer = (state = { num: 123 }, action) = > {
  // What type of action is passed in
  switch (action.type) {
    case addAction:
      return Object.assign({}, state, action);
    default:
      return state;
  }

  // Do nothing, return the state passed in (initData by default)
  // return state;
};

export { counterReducer };
Copy the code

Create the store

The introduction of the file

import { createStore } from "redux";
import { counterReducer } from "./reducers";
Copy the code

createStore

const store = createStore(
  counterReducer /* preloadedState, */.window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default store;
Copy the code

Distributing the action

const handleClick = () = > {
  console.log('Clicked the button');
  const action = addAction({ num: "456" });
  store.dispatch(action);
};
Copy the code

Listening to the

useEffect(() = > {
  store.subscribe(() = > {
    console.log("-- -- -- -- --", store.getState()); }); } []);Copy the code

Changes to the subscription status

const render = () = > {
  ReactDom.render(<App />.document.querySelector("#root"));
};
// Render once on the way up
render();
// Subscribe to changes and re-render every time the data changes
store.subscribe(render);
Copy the code

summary

Through a simple case, we know a simple process:

  1. First build aactionReturn an objectThere must be a Type attribute
  2. buildreducerThe response action T returns the data back to the Store via a return
  3. usingreduxThis library creates a store to pass writtenreducer
  4. By using the$store.subscribe()Register to monitor
  5. Can be achieved bystore.getState()The values

React-Redux

That looks fine in the redux library we used above, but

  • The first step is to import store
  • And then sign up to listen
  • We then cancel listening when the component is destroyed

React-redux: React-redux: React-redux: React-redux: React-redux: React-Redux: React-Redux

What is thereact-redux

  • Official product of Redux

  • React is used to manage data

The Provider component

  • Wrap around the outermost component so that all child components can be reachedstate
  • receivestoreAs apropsthroughcontextpass
<Provider store={store}>
  <App />
</Provider>
Copy the code

mapStateToProps(state,ownProps)

const mapStateToProps = (state, ownProps) = > {
  console.log(state);
  return state;
  // return {
  // prop: state.prop
  // }
};
Copy the code

mapDispathToProps(dispath,ownProps)

const mapDispatchToProps = (dispatch, ownProps) = > {
  return {
    sendAction: () = > {
      dispatch({
        type: "ADD_TYPE"}); }}; };Copy the code

The connect method

  • Component Internal AcquisitionstoreIn thestate
  • throughconnectTo strengthen
export default connect(
  (state, { history, location, match, staticContext }) = > ({
    ...state,
  }),
  (dispatch, { history, location, match, staticContext }) = > ({
    add() {
      dispatch(
        actionCreator({
          num: 10,})); }, }) )(Home);Copy the code

The complete code

import React, { memo, useEffect } from "react";
import axios from "axios";
import { connect } from "react-redux";

const ADD_TYPE = "ADD_TYPE";
const actionCreator = (params) = > ({
  type: ADD_TYPE,
  payload: {
    ...params,
  },
});
const InitialState = {
  counter: 0};export const counterReducer = (state = InitialState, { type, payload }) = > {
  switch (type) {
    case ADD_TYPE:
      console.log(state);
      return {
        ...state,
        counter: state.counter + payload.num,
      };
    default:
      returnstate; }};const Home = ({ history, location, match, staticContext, counter, add }) = > {
  useEffect(() = > {
    return () = >{}; } []);const handleClick = (e) = > {
    add();
  };
  return (
    <>
      <p>This is the component</p>
      <h3>{counter}</h3>
      <button
        onClick={(e)= >{ handleClick(e); }} > Click love</button>
    </>
  );
};

export default connect(
  (state, { history, location, match, staticContext }) = > ({
    ...state,
  }),
  (dispatch, { history, location, match, staticContext }) = > ({
    add() {
      dispatch(
        actionCreator({
          num: 10,})); }, }) )(Home);Copy the code

use

  • Install related dependencies

  • Build store and Readucer

  • Provider component implementation

<>
  <Provider store={store}>
    <List></List>
    <Detail></Detail>
  </Provider>
</>
Copy the code
  • connect

combineReducers

  • Function, which takes one argument
  • Split reducer
import { createStore, combineReducers } from "redux";
// import { counterReducer } from "./reducers";
// import rootReducer from './reducers/index'

import { infoReducer } from "./reducers/infoReducer";
import { listReducer } from "./reducers/listReducer";

const reducer = combineReducers({
  infoReducer,
  listReducer,
});

/ / build a store
const store = createStore(reducer);
export default store;
Copy the code

Create components

  • ComA A component

    import React, { Component } from "react"; import { connect } from "react-redux"; class ComA extends Component { handleClick = () => { this.props.getInfo(); }; Render () {return (< div > {/ * < h3 > {this. Props.} < / h3 > * /} < button onClick = {this. HandleClick} > information < / button > < / div >). } } const mapStateToProps = (state, ownProps) => { console.log(state.infoReducer); // return { // prop: state.prop, // }; // return state return { ... state.infoReducer, }; }; const mapDispatchToProps = (dispatch, ownProps) => { return { getInfo: () => { const actionCreator = { type: "GET_INFO", }; dispatch(actionCreator); }}; }; export default connect(mapStateToProps, mapDispatchToProps)(ComA);Copy the code
  • ComB

    import React, { Component } from "react"; import { connect } from "react-redux"; class ComB extends Component { handleClick = () => { this.props.getList(); }; Render () {return (<div> <button onClick={this.handleclick}> get list </button> </div>); } } const mapStateToProps = (state, ownProps) => { console.log(state.listReducer) // return state return { ... state.listReducer } }; const mapDispatchToProps = (dispatch, ownProps) => { return { getList: () => { const actionCreator = { type: "GET_LIST", }; dispatch(actionCreator); }}; };Copy the code

export default connect(mapStateToProps, mapDispatchToProps)(ComB);

! [](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/4/16/17180a5f0b412611~tplv-t2oaga2asx-image.image ) - infoReducer.js ```js const info = { name: "yayxs", }; const infoReducer = (state = {}, action) => { switch (action.type) { case "GET_INFO": return { ... info, }; default: return state; }}; export { infoReducer };Copy the code
  • listReducer

    const listArr = [
      {
        id: 1.con: "Headphones",},];const listReducer = (state = {}, action) = > {
      switch (action.type) {
        case "GET_LIST":
          return {
            listArr: [...listArr],
          };
    
        default:
          returnstate; }};export { listReducer };
    Copy the code

Three, the story – Saga

Anyway, as mentioned above, data flow operations only support synchronous operations, which requires middleware to implement asynchronous operations

1. The middleware

  • It’s a function in itself
  • Apply after the Action is published

Summary of 2.

  • To manage side effects, which include things likeAsynchronous operationsTo make the implementation of side effects easier
  • Es6 grammar, refer to Ruan teacher

3. createSagaMiddleware

The source code looks like this

export default function createSagaMiddleware<C extends object> (options? : SagaMiddlewareOptions
       ) :SagaMiddleware<C>;

export interface SagaMiddlewareOptions<C extends object = {}> {
  /** * Initial value of the saga's context. */context? : C;/** * If a Saga Monitor is provided, the middleware will deliver monitoring * events to the monitor. */sagaMonitor? : SagaMonitor;/** * If provided, the middleware will call it with uncaught errors from Sagas. * useful for sending uncaught exceptions to error tracking services. */onError? (error:Error.errorInfo: ErrorInfo): void;
  /** * Allows you to intercept any effect, resolve it on your own and pass to the * next middleware. */effectMiddlewares? : EffectMiddleware[]; }Copy the code

The import

import createSagaMiddleware from "redux-saga";
Copy the code

Building a store

const store = createStore(sagaReducer, {}, applyMiddleware(sagaMiddleware));
Copy the code
  • The first parameter is reducer
  • The second initState
  • Third parameter: middleware

perform

sagaMiddleware.run(defSage);
Copy the code

Case 4.

Saga’s helper function

  • takeEvery

  • takeLatest

  • throttle

  • SagaCom

handleClick = (type) = > {
  switch (type) {
    case "takeEvery":
      this.props.dispatch({
        type: "takeEvery"});break;
    case "takeLatest":
      this.props.dispatch({
        type: "takeLatest"});break;

    case "throttle":
      this.props.dispatch({
        type: "throttle"});break;

    default:
      break; }};Copy the code
  • sages/index.js
import {
  takeEvery,
  takeLatest,
  throttle,
  select,
  call,
} from "redux-saga/effects";

import axios from "axios";
export function* defSage() {
  yield takeEvery("takeEvery".function* () {
    const state = yield select((state) = > state.payload);

    const res = yield call(
      axios.post,
      `http://rap2.taobao.org:38080/app/mock/249413/mock-api/v1/users/login`,
      {
        ...state,
      }
    );

    console.log(res);
  });
  // Last time, cancel is running
  yield takeLatest("takeLatest".function* () {
    const state = yield select((state) = > state.payload);

    const res = yield call(
      axios.post,
      `http://rap2.taobao.org:38080/app/mock/249413/mock-api/v1/users/login`,
      {
        ...state,
      }
    );

    console.log(res);
  });
  /** * milliseconds */
  yield throttle(0."throttle".function* () {
    const state = yield select((state) = > state.payload);

    const res = yield call(
      axios.post,
      `http://rap2.taobao.org:38080/app/mock/249413/mock-api/v1/users/login`,
      {
        ...state,
      }
    );

    console.log(res);
  });
}
Copy the code

Effect creator

Detailed API usage can be found in the official documentation

  • select
  • call
  • take
  • put

The business process

To get the data

  • As soon as the page loads, then send one to get the dataaction
  • inreducerIf the action matches the corresponding action, the data is returned directly
  • Used in SagatakeEveryTo listen in.
  • The call method invokes an asynchronous request, passing in the parameters of the request
  • Put Side Effect Indicates whether sending the action succeeds or fails
  • Process the actions in the Reducer

The life cycle

  • ComponentDidMount gets data
  • ComponentWillUpdate handles data

Four, thinking

  1. Hooks API: how do functional components listen for page data changes and then refresh?
  2. Helper functions in Redux-SagatakeEvery takeLatest throttleWhat’s the difference at the bottom?

Thank you for seeing this. Might as well give me a star. Thank you