Design principles and ideas

  • A single data source facilitates communication between React components and state management in a unified manner
  • One-way data flow, to ensure that the data is pure, not polluted by other operations.
  • Redux stores the entire application state in one place, called a Store, which holds a state tree called State
  • The component sends actions to the Reducer in the Store. The Reducer calculates and returns the new state. The Store updates the state and notifies the subscribed components to re-render
  • Other components can refresh their views by subscribing to states in the Store

First of all usereact-cliScaffolding to create a project

  • Delete useless files
  • The installationredux
npx create-react-app redux-hand
yarn add redux
Copy the code

Use the original firstreduxImplement a simple counter

  • createreducerandstore
  • createAppComponent, subscribes to SOTRE when the component is mounted and unsubscribes before the component is destroyed
  • Click on the+/-The buttondispatchAn action (containstypeandpayloadTwo properties) to modify the state in the store
  • After updating the status in store, executesubscribeFunction to update the component state
import React from "react";
import ReactDom from "react-dom";
import { createStore } from "redux";

// Dispatch the action
const INCREMENT = "INCREMENT";
const DECREMENT = "DECREMENT";

// Initial state
let initState = { number: 0 };

const reducer = (state = initState, action) = > {
  switch (action.type) {
    case INCREMENT:
      return { number: state.number + 1 };
    case DECREMENT:
      return { number: state.number - 1 };
    default:
      returnstate; }};Create a store based on the reducer
let store = createStore(reducer);

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { number: 0 };
  }

  componentDidMount() {
    // Subscribe to sotre when the component is mounted
    this.unsubscribe = store.subscribe(() = >
      this.setState({ number: store.getState().number })
    );
  }
  componentWillUnmount() {
    // Unsubscribe before component destruction
    this.unsubscribe();
  }

  render() {
    return (
      <div style={{ margin: "30px}} ">
        <h2>{this.state.number}</h2>
        <button title="Add" onClick={()= >Store.dispatch ({type: INCREMENT})}> +</button>
        <button title="Cut" onClick={()= >Store.dispatch ({type: DECREMENT})}> -</button>
      </div>
    );
  }
}

ReactDom.render(<App />.document.getElementById("root"));
Copy the code

Implement core methods in Redux

1, implementation,createStore

  • createStoreUsed to createstoreObject, for external use, usually only one in an applicationstore
  • createStoreMethod returns an object containing three methods
  • getStateRetrieves the state in store and returns the state in store
  • subscribeSubscribe, which saves the subscribed functions incurrentListeners, and then returnunsubscribeFunction unsubscribe (that is, the subscribed function fromcurrentListeners).
  • dispatchWhen an action is issued, it will be executed firstreducerFunction to get the latest state and then execute fromcurrentListenersThe subscribed function in.
import ActionTypes from "./utils/actionTypes";

export default function createStore(reducer, preloadedState) {
  let currentReducer = reducer;
  let currentState = preloadedState;
  let currentListeners = [];
  function getState() {
    return currentState;
  }

  function subscribe(listener) {
    currentListeners.push(listener);

    return function unsubscribe() {
      const index = currentListeners.indexOf(listener);
      currentListeners.splice(index, 1);
    };
  }

  function dispatch(action) {
    currentState = currentReducer(currentState, action);
    for (let i = 0; i < currentListeners.length; i++) {
      const listener = currentListeners[i];
      listener();
    }
    return action;
  }

  dispatch({ type: ActionTypes.INIT });

  const store = {
    dispatch,
    subscribe,
    getState,
  };
  return store;
}
Copy the code

The action is a normal object. What if we want to send a function, or do an asynchronous change

2. Let’s look at bindActionCreators

  • The statementaddandminusTwo functions, the return value isactionobject
  • callbindActionCreatorsMethod will beactionsandstore.dispatchMethod to bind
function add() {
  return { type: INCREMENT };
}
function minus() {
  return { type: DECREMENT };
}
const actions = { add, minus };
const boundActions = bindActionCreators(actions, store.dispatch);
Copy the code
  • Used in the render functionboundActions
render() {
    return (
      <div style={{ margin: "30px}} ">
        <h2>{store.getState().number}</h2>
        <button title="Add" onClick={()= >Store.dispatch ({type: INCREMENT})}> +</button>
        <button title="Cut" onClick={()= >Store.dispatch ({type: DECREMENT})}> -</button>
        <button
          title=Asynchronous plus one
          onClick={()= >SetTimeout (boundactions.add, 1000)} > Async +</button>
      </div>
    );
  }
Copy the code

3, implementation,bindActionCreator

  • You’re essentially taking the function that’s passed indispatchMethod is wrapped once and returns a new function
  • It is available directly in the bound functiondispatchMethod to modify the state in the store
function bindActionCreator(actionCreator, dispatch) {
  return function (. args) {
    return dispatch(actionCreator.apply(this, args));
  };
}

export default function bindActionCreators(actionCreators, dispatch) {
  // A single function is passed in, bound directly and returned
  if (typeof actionCreators === "function") {
    return bindActionCreator(actionCreators, dispatch);
  }
  const boundActionCreators = {};
  for (const key in actionCreators) {
    const actionCreator = actionCreators[key];
    if (typeof actionCreator === "function") { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch); }}return boundActionCreators;
}
Copy the code

4. There can only be one storereducerandstate, needed when we have multiple modules and multiple reducercombineReducersrightreducerLet’s see how we can use it. Okay

  • For the original project pressreduxThe actual use in the project is transformed, and the final catalog is as follows

  • Reducer of Counter1 is similar to that of Counter2
import * as types from ".. /action-types";
let initialState = { number: 0 };
export default function (state = initialState, action) {
  switch (action.type) {
    case types.ADD1:
      return { number: state.number + 1 };
    case types.MINUS1:
      return { number: state.number - 1 };
    default:
      returnstate; }}Copy the code
  • Merge counter1 and Counter2reducer
// src/store/reducers/index.js
import { combineReducers } from "redux";
import counter1 from "./counter1";
import counter2 from "./counter2";
let rootReducer = combineReducers({
  counter1,
  counter2,
});
export default rootReducer;
Copy the code
  • According to the mergedreducerCreate the store
import { createStore } from ".. /redux";
import reducer from "./reducers";
const store = createStore(reducer);
export default store;
Copy the code

5, implementation,combineReducers

  • combineReducersPhi is a higher-order function, which essentially gives phistoreGenerates a mergedThe total state
  • nextStateWill eachreducerKey as the key, eachreducerThe returnedPoints in the stateStored as valuenextStatePerform the merge as return tostoreThe final state of
  • whendispatchaaction, will be handed over firstcombinationFunction,combinationWill be inactionPass to allreducer, returns the latestPoints in the stateAnd put them innextStateBack to thestore
function combineReducers(reducers) {
  return function combination(state = {}, action) {
    let nextState = {};
    for (let key in reducers) {
      nextState[key] = reducers[key](state[key], action);
    }
    return nextState;
  };
}
export default combineReducers;
Copy the code

Will be used in the projectreduxSwitch to your own implementationreduxThe net effect is the same

The code address

Next section: Implementing React-redux from 1

If this article is helpful to you, please give me a thumbs up