zoukankan      html  css  js  c++  java
  • React 学习(十一) Redux

    Redux

    • Single Data Source
      • All states is stored in an Object Tree, and the Object Tree is stored in only one store
      • Easy to maintain, track and modify
    • State is read-only
      • The only way to change states is to use action
    • Use pure function

    Redux Process

    sequenceDiagram participant v as view participant r as reducer participant s as state v->>r: dispatch(action) r->>s: change state s->>s: Shallow comparison and Update s->>v: call render() to update view
    • Basic Usage

      // node index.js
      const redux = require("redux");
      
      // store
      const store = redux.createStore(reducer);
      
      // state
      const initState = {
        counter: 0,
      };
      
      // subscribe
      store.subscribe(() => {
        console.log("counter", store.getState().counter);
      });
      
      // reducer
      const reducer = (state = initState, action) => {
        const { type, payload } = action;
        switch (type) {
          case "INCREMENT":
            return { ...state, counter: state.counter + 1 };
          case "DECREMENT":
            return { ...state, counter: state.counter + 1 };
          case "ADD_NUMBER":
            return { ...state, counter: state.counter + payload.counter };
          case "SUB_NUMBER":
            return { ...state, counter: state.counter - payload.counter };
      
          default:
            return state;
        }
      };
      
      // action
      const action1 = () => ({
        type: "INCREMENT",
      });
      
      const action2 = () => ({
        type: "DECREMENT",
      });
      
      const action3 = (num) => ({
        type: "ADD_NUMBER",
        payload: {
          num,
        },
      });
      
      const action4 = (num) => ({
        type: "SUB_NUMBER",
        payload: {
          num,
        },
      });
      
      // dispatch
      store.dispatch(action1());
      store.dispatch(action1());
      store.dispatch(action2());
      store.dispatch(action2());
      store.dispatch(action3(5));
      store.dispatch(action4(12));
      
    • Module Redux

      /**
       * │  index.js
       * │  package-lock.json
       * │  package.json
       * └─store
       *         actionCreator.js
       *         constants.js
       *         index.js
       *         reducer.js
       */
      
      // store
      // index.js
      import redux from "redux";
      import reducer from "./reducer.js";
      const store = redux.createStore(reducer);
      export default store;
      
      // actionCreator.js
      import { ADD_NUMBER, SUB_NUMBER } from "./constants.js";
      
      export const addAction = (num) => ({
        type: ADD_NUMBER,
        num,
      });
      
      export const subAction = (num) => ({
        type: SUB_NUMBER,
        num,
      });
      
      // reducer.js
      import { ADD_NUMBER, SUB_NUMBER } from "./constants.js";
      const defaultState = {
        counter: 0,
      };
      
      function reducer(state = defaultState, action) {
        switch (action.type) {
          case ADD_NUMBER:
            return { ...state, counter: state.counter + action.num };
          case SUB_NUMBER:
            return { ...state, counter: state.counter - action.num };
          default:
            return state;
        }
      }
      
      export default reducer;
      
      // index.js
      import store from "./store/index.js";
      import { addAction, subAction } from "./store/actionCreator.js";
      
      store.subscribe(() => {
        console.log(store.getState());
      });
      
      store.dispatch(addAction(10));
      store.dispatch(addAction(15));
      store.dispatch(addAction(10));
      store.dispatch(subAction(20));
      store.dispatch(subAction(3));
      store.dispatch(subAction(1));
      

    Connect(HOC)

    // App.js
    const mapStateToProps = (state) => ({
      xxx: xxx
    });
    
    const mapDispatchToProps = (dispatch) => ({
      func() {
        dispatch(action())
      }
    });
    
    export default connect(mapStateToProps, mapDispatchToProps)(App);
    
    // Basic connect.js
    import React, { PureComponent } from "react";
    import store from "../store";
    
    export const connect = (mapStateToProps, mapDispatchToProps) => {
      return (WrappedComponent) => {
        return class extends PureComponent {
          constructor(props) {
            super();
    
            this.stateStore = {
              // Listen the incoming state change through own state, and call render function to update
              stateStore: mapStateToProps(store.getState()),
            };
          }
    
          componentDidMount() {
            this.unSubscribe = store.subscribe(() => {
              this.setState({
                stateStore: mapStateToProps(store.getState()),
              });
            });
          }
    
          componentWillUnmount() {
            this.unSubscribe();
          }
    
          render() {
            return (
              <WrappedComponent
                {...this.props}
                {...mapStateToProps(store.getState())}
                {...mapDispatchToProps(store.dispatch)}
              />
            );
          }
        };
      };
    };
    

    props and context[^props]

    // Advanced(Use Context) connect.js
    // context.js
    import React from "react";
    const StoreContext = React.createContext();
    export { StoreContext };
    
    // connect.js
    import React, { PureComponent } from "react";
    import { StoreContext } from "./context";
    
    export const connect = (mapStateToProps, mapDispatchToProps) => {
      return (WrappedComponent) => {
        class EnhanceComponent extends PureComponent {
          constructor(props, context) {
            super();
    
            this.stateStore = {
              // This.context hasn't been assigned here yet
              stateStore: mapStateToProps(context.getState()),
            };
          }
    
          componentDidMount() {
            this.unSubscribe = this.context.subscribe(() => {
              this.setState({
                stateStore: mapStateToProps(this.context.getState()),
              });
            });
          }
    
          componentWillUnmount() {
            this.unSubscribe();
          }
    
          render() {
            return (
              <WrappedComponent
                {...this.props}
                {...mapStateToProps(this.context.getState())}
                {...mapDispatchToProps(this.context.dispatch)}
              />
            );
          }
        }
    
        EnhanceComponent.contextType = StoreContext;
    
        return EnhanceComponent;
      };
    };
    
    // index.js
    import React from "react";
    import ReactDOM from "react-dom";
    import App from "./App";
    import store from "./store";
    import { StoreContext } from "./utils/context";
    
    ReactDOM.render(
      <StoreContext.Provider value={store}>
        <App />
      </StoreContext.Provider>,
      document.getElementById("root")
    );
    
    

    Middleware

    • redux-thunk

      dispatch(action Object ==> action Function): We can make network requests in the funciton

      // App.js
      import { getHomeMultidataAction } from "../store/home/actionCreator";
      const mapDispatchToProps = (dispatch) => ({
        // What we pass in here is a function, not an obejct
        // and we do not need to call the function manually
        getHomeMultidata() {
          dispatch(getHomeMultidataAction);
        },
      });
      
      // actionCreator.js
      import axios from "axios";
      import { CHANGE_BANNER, CHANGE_RECOMMEND } from "./constants.js";
      export const changeBannerAction = (banner) => ({
        type: CHANGE_BANNER,
        banner,
      });
      export const getHomeMultidataAction = (dispatch, getState) => {
        axios({
          url: "http://123.207.32.32:8000/home/multidata",
        }).then((res) => {
          const { data } = res.data;
          // console.log(data);
          dispatch(changeBannerAction(data.banner.list));
        });
        // console.log(getState());
      };
      
      // reducer.js
      import { CHANGE_BANNER, CHANGE_RECOMMEND } from "./constants.js";
      // home
      const initalHomeState = {
        banner: [],
        recommend: [],
      };
      function homeReducer(homeInfo = initalHomeState, action) {
        switch (action.type) {
          case CHANGE_BANNER:
            return { ...homeInfo, banner: action.banner };
          case CHANGE_RECOMMEND:
            return { ...homeInfo, recommend: action.recommend };
          default:
            return homeInfo;
        }
      }
      export default homeReducer;
      
    • redux-saga

      // index.js
      import { createStore, applyMiddleware } from "redux";
      import reducer from "./reducer";
      // thunk
      import thunkMiddleware from "redux-thunk";
      // saga
      import createSagaMiddleware from "redux-saga";
      import mySaga from "./home/saga";
      const sagaMiddleware = createSagaMiddleware();
      const storeEnhancer = applyMiddleware(thunkMiddleware, sagaMiddleware);
      const store = createStore(reducer, storeEnhancer);
      // generator Function
      sagaMiddleware.run(mySaga);
      export default store;
      
      // saga.js
      import axios from "axios";
      import { all, put, takeEvery } from "redux-saga/effects";
      import { FETCH_GET_MULTIDATA } from "./constants";
      import { changeBannerAction, changeRecommendAction } from "./actionCreator";
      
      function* fetchGetMultidata(action) {
        const {
          data: { data },
        } = yield axios({
          url: "http://123.207.32.32:8000/home/multidata",
        });
        yield all([
          put(changeBannerAction(data.banner.list)),
          put(changeRecommendAction(data.recommend.list)),
        ]);
      }
      function* mySaga() {
        // takeLaste: Excute the latest one
        // takeEvery: Every will be excuted
        // action.type, generator
        yield takeEvery(FETCH_GET_MULTIDATA, fetchGetMultidata);
      }
      export default mySaga;
      
      // actionCreator.js
      export const fetchGetMultidataAction = () => ({
        type: FETCH_GET_MULTIDATA,
      });
      
      // App.js
      const mapDispatchToProps = (dispatch) => ({
        // Pass in an object
        fetchGetMultidata() {
          dispatch(fetchGetMultidataAction());
        },
      });
      
    每天进步一点点
  • 相关阅读:
    [Maid] Write Tasks in Markdown with Maid
    [React] Preview and edit a component live with React Live
    [React] Build a slide deck with mdx-deck using Markdown + React
    [React] Spread Component Props in JSX with React
    重载new delete操作符是怎么调用的
    oracle如何设置show parameter显示隐含参数
    Google用户登录界面 Android实现
    Jquery 动态生成表单 并将表单数据 批量通过Ajax插入到数据库
    消息机4_B
    jQuery中对 input 控件的操作
  • 原文地址:https://www.cnblogs.com/smallstars/p/14074902.html
Copyright © 2011-2022 走看看