knowledge

Pure functions

  1. If the function is called with the same arguments, the same result is always returned.
  2. It does not depend on any changes in state or data outside the function during program execution and must depend only on its input parameters.
  3. This function does not produce any observable side effects, such as network requests, input and output devices, or data mutations.

Reduce

const array1 = [1, 2, 3, 4]; const reducer = (accumulator, currentValue) => accumulator + currentValue; // 1 + 2 + 3 + 4 console.log(array1.reduce(reducer)); Expected Output: 10 // 5 + 1 + 2 + 3 + 4 console.log(array1.reduce(reducer, 5)); // Expected output: 15Copy the code

Reducer

  1. Reducer is a pure function that receives the old state and action and returns the new state.
(previousState, action) => newState
Copy the code
  1. This function is called reducer because it is the same as array.prototype. reduce(reducer,? InitialValue)

Same type. 3. It is important to keep reducer purity. Do not modify incoming parameters on the Reducer, do not perform operations with side effects such as API requests and jump routes, and do not call impure functions such as date.now () or math.random ().

Use redux

Simple synchronous data flow scenarios

Install the story

yarn add redux
Copy the code

use

  1. You need a store to store data
  2. The Reducer in the store initializes state and defines state modification rules
  3. Submit changes to the data by dispatching an action
  4. Actions are submitted to the Reducer function and new states are returned based on the action type that was passed in.

Create a store – SRC/store/index. Js

import {createStore} from "redux";

function countReducer(state = 0, action) {
  switch (action.type) {
    case "ADD":
      return state + 1;
    case "MINUS":
      return state - 1;
    default:
      return state;
  } 
}
const store = createStore(countReducer);
export default store;
Copy the code

Create ReduxPage

import React, {Component} from "react";
import store from "../store/";
export default class ReduxPage extends Component {

  componentDidMount() {
    store.subscribe(() => {
      this.forceUpdate();
    });
  }
  
  add = () => {
    store.dispatch({type: "ADD"});
  };
  
  minus = () => {
    store.dispatch({type: "MINUS"});
  }; 
  
  render() {
    console.log("store", store); //sy-log
    return (
      <div>
        <h3>ReduxPage</h3>
        <p>{store.getState()}</p>
        <button onClick={this.add}>add</button>
        <button onClick={this.minus}>minus</button>
      </div>
    ); 
  }
}
Copy the code

Note: If the button cannot be updated, check to see if the subscribe status changes.

You can also subscribe to state changes in SRC /index.js render

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import store from './store/'

const render = () => ReactDOM.render(<App />, document.getElementById("root"));

render()

store.subscribe(render)
Copy the code

checkpoint

  1. CreateStore create store
  2. Reducer initialized and modified the state function
  3. GetState Gets the status value
  4. Dispatch submit update
  5. Subscribe to

Redux source

The core to realize

  1. Storage status State
  2. Get the state getState
  3. Update status Dispatch
  4. Change subscribe

The core code

Export default function createStore(reducer, enhancer) {if (enhancer) {// Export default function createStore(reducer, enhancer) { Return enhancer(reducer), such as callback and Promise; } let currentState; let currentListeners = []; function getState() { return currentState; } function dispatch(action) { currentState = reducer(currentState, action); currentListeners.forEach(listener => listener()); } function subscribe(listener) { currentListeners.push(listener); Return () => {// currentListeners = []; }; } // Return the initial dispatch({type: "REDUX/ZXB"}); Return {getState, // getState dispatch, // trigger change state subscribe // subscribe}; }Copy the code

asynchronous

Redux is a pure state manager that only supports synchronization by default. Implementing asynchronous tasks such as delay and network requests requires middleware support. For example, we use the simplest redux-thunk and redux-Logger, redux-Promise, etc

The middleware

  1. The middleware is a function that modifies the store.dispatch method by adding additional capabilities between the actions issued and the Reducer steps.
  2. Redux’s Middleware came to power Dispatch

Application middleware

  yarn add redux-thunk redux-logger
Copy the code
 
import { createStore, applyMiddleware } from "redux";
import logger from "redux-logger";
import thunk from "redux-thunk";
import counterReducer from './counterReducer'
const store = createStore(counterReducer,applyMiddleware(thunk, logger));
Copy the code
  1. Implement applyMiddleware
// Middleware's unusual design is an anonymous function wrapped in layers. // This is the curry of functional programming. // A way to implement multi-parameter functions using a hidden list of parameter functions. // applyMiddleware makes layer upon layer calls to Logger, dynamically assigning values to store and next parameters. export default function applyMiddleware(... middlewares) { return createStore => reducer => { const store = createStore(reducer); let dispatch = store.dispatch; const midApi = { getState: store.getState, dispatch: (action, ... args) => dispatch(action, ... args) }; // Step 2: Map makes each middleware run separately with midApi to get the chain array, [F1, F2,... fx,... fn], and each anonymous function can access the same store because of the closure. MiddlewareAPI namely. const middlewareChain = middlewares.map(middleware => middleware(midApi)); // Middlewares: compose assembles all the anonymous functions in the chain, [F1, F2... FX... fn], into a new function, That is, the new dispatch // when the new dispatch is executed, [F1, F2... FX... fn] is executed from left to right (so the order is important) // The dispatch is augmented by the dispatch = compose(... middlewareChain)(store.dispatch); return { ... Store, // return the enhanced dispatch}; }; } // compose(... Funcs (chain) returns an anonymous function. Funcs is the chain array. The function (fx) composed is the result of the previous fx+1 execution. The arg of fn (n represents the length of chain) executed for the first time is store.dispatch. // So when compose is finished, we should have a dispatch like this. Assuming n = 3, we should have: Dispatch = f1(f2(f3(store.dispatch)))) // Call a new dispatch. funcs) { if (funcs.length === 0) { return args => args; } if (funcs.length === 1) { return funcs[0]; } return funcs.reduce((composed, func) => (... args) => composed(func(... args))); }Copy the code

Conclusion: The heart of the applyMiddleware mechanism is the compose combination, which bundles different middlewares layer by layer on top of the native dispatch. The middleware should be designed in a curry-like way to dynamically generate next methods and maintain store consistency. Because stores are as easily accessible in Middleware as they are outside, you can use the state of the current store to make conditional judgments and use the dispatch method to intercept old actions or send new ones.

  1. Implement redux – thunk
/ /! Next is the aggregate function function thunk({dispatch, getState}) { return next => action => { if (typeof action === "function") { return action(dispatch, getState); } return next(action); }; }Copy the code
  1. Implement redux – logger
Function Logger ({getState}) {return next => action => {console.log(action.type + "executed!") ); //sy-log let prevState = getState(); console.log("prev state", prevState); //sy-log // state const returnValue = next(action); let nextState = getState(); console.log("next state", nextState); //sy-log return returnValue; }; }Copy the code
  1. redux-promise
import isPromise from "is-promise";

function promise({dispatch}) {
  return next => action => {
    return isPromise(action) ? action.then(dispatch) : next(action);
  };
}
Copy the code