• redux的实现(包含combineRerucers、applyMiddleware、react-redux的connect)


    概述

    Redux的核心思想很简单,就是一个发布订阅监听数据的变化,再限定只能通过dispatch去更改数据。redux的三大特性这里再提一下:

    • 数据来源唯一
    • 数据只读,只能通过dispatch修改数据
    • reducer是一个纯函数

    文章将按照下面的步骤进行:

    1. 实现一个发布订阅
    2. 实现combineReducers对reducer进行切片
    3. 实现react-redux的connect函数,将发布订阅和视图渲染关联到一起
    4. 实现applyMiddleware

    一、发布订阅

    import { useEffect, useState } from 'react';
    
    function createStore(reducer, initState) {
      function dispatch(action) {
        const newState = reducer(this.state, action);
        this.state = newState;
        this.listeners.forEach(fn => fn());
      };
    
      function getState() {
        return this.state;
      };
    
      function subscribe(fn) {
        this.listeners.push(fn);
      };
    
      const store = {
        listeners: [],
        state: initState,
        dispatch,
        getState,
        subscribe,
      };
      return store;
    };
    
    const reducer = (state = {}, action) => {
      const todoList = state?.todoList || [];
      switch (action.type) {
        case 'ADD': return { ...state, todoList: [...todoList, 'newData'] };
        case 'DELETE': return { ...state, todoList: [...todoList].slice(1) };
        default: return state;
      }
    };
    
    // 使用
    const initData = {
      todoList: ['apple', 'orange', 'banana', 'grape', 'watermelon']
    };
    const myStore = createStore(reducer, initData);
    
    function App() {
    
      useEffect(() => {
        myStore.subscribe(() => {
          console.log('订阅数据', myStore.getState());
        });
      }, []);
    
      return (
        <div className="App">
          <button onClick={() => myStore.dispatch({type:'ADD'})}>添加</button>
          <button onClick={() => myStore.dispatch({type:'DELETE'})}>删除</button>
        </div>
      );
    }
    
    export default App;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    点击按钮进行添加和删除操作
    在这里插入图片描述

    二、combineReducers的实现

    const combineReducers = (reducers) => (state, action) => {
      const reducerKeys = Object.keys(reducers);
      let finalReducers = {};
      for (let i = 0; i < reducerKeys.length; i++) {
        const key = reducerKeys[i];
        if (typeof reducers[key] === 'function') {
          finalReducers[key] = reducers[key]
        }
      };
    
      let hasChanged = false;
      const nextState = {};
      const finalReducerKeys = Object.keys(finalReducers);
    
      for (let i = 0; i < finalReducerKeys.length; i++) {
        const key = finalReducerKeys[i];
        const previousStateForKey = state[key];
        const reducer = finalReducers[key];
        const nextStateForKey = reducer(previousStateForKey, action);
        nextState[key] = nextStateForKey;
        hasChanged = hasChanged || previousStateForKey !== nextStateForKey;
      };
      return hasChanged ? nextState : state;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    改写reducer,进行分片

    const todoListReducer = ( state=[],action)=>{
       switch(action.type){
         case 'ADD': return [...state,'newData'];
         case 'DELETE': return [...state].slice(1);
         default :return state;
       };
    };
    
    const userInfoReducer=(state={},action)=>{
     switch(action.type){
      case 'ADD': return action.userInfo;
      case 'DELETE': return {};
      default :return state;
     }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    引入的地方进行改写

    const appReducer = combineReducers({
       todoList:todoListReducer,
       userInfo:userInfoReducer,
    });
    
    const myStore = createStore(appReducer, initData);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    三、react-redux的实现

    前面我们已经实现了发布订阅,接下来需要将数据的变化和store的state关联起来。实现一个connect函数将store中高的state和dispatch映射到组件的props。connect是一个高阶组件,接收store中的state和dispatch并将它们映射到组件的props。

    function connect(mapStateToProps, mapDispatchToProps) {
      return function (WrapComponent) {
        return function (props) {
    
          const [state,setState] = useState(mapStateToProps(myStore.getState()));
          const dispatch = mapDispatchToProps(myStore.dispatch.bind(myStore))
         
          useEffect(()=>{
            myStore.subscribe(()=>{
               setState(mapStateToProps(myStore.getState()));
            })
          },[]);
          
          return <WrapComponent {...props}  {...dispatch} {...state} />
        }
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在组件中使用

    
    const mapStateToProps = (state) => {
      return {
        todoList: state.todoList,
        userInfo: state.userInfo
      };
    };
    
    const mapDispatchToProps = (dispatch) => {
      return {
        addTodo: () => dispatch({ type: 'ADD' }),
        deleteToDo: () => dispatch({ type: 'DELETE' }),
      };
    };
    
    function List(props) {
      useEffect(() => {
        // store中的数据变化时通过更新组件的state渲染视图
        myStore.subscribe(() => {
          console.log('订阅数据', myStore.getState());
        });
      }, []);
    
      const renderList = useMemo(() => {
        return props.todoList.map((todo,index) => <div key={index}>{todo}</div>)
      }, [props.todoList]);
    
      return (
        <div className="List">
          <button onClick={()=> props.addTodo()}>添加</button>
          <button onClick={()=> props.deleteToDo()}>删除</button>
          {renderList}
        </div>
      );
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(List);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    现在点击增加和删除,页面视图也会跟着更新

    在这里插入图片描述

    四、applyMiddleware的实现

    applyMiddleware的实现上篇文章中讲过了,这里不再多讲直接上代码

    const applyMiddleware = middlewares => store=>{
      middlewares.forEach(middleware => {
        store.dispatch = middleware(store)(store.dispatch);
      });
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在createStore中调用applyMiddleWare

    function createStore(reducer, initState, applyMiddleware) {
    
      function dispatch(action) {
        const newState = reducer(this.state, action);
        this.state = newState;
        this.listeners.forEach(fn => fn());
      };
    
      function getState() {
        return this.state;
      };
    
      function subscribe(fn) {
        this.listeners.push(fn);
      };
    
      const store = {
        listeners: [],
        state: initState,
        dispatch,
        getState,
        subscribe,
      };
    
      //添加中间件
      if(typeof applyMiddleware ==='function') applyMiddleware(store);
      return store;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    使用log中间件和thunk中间件,现在可以dispatch一个对象或者函数了。

    // 日志中间件
    const logger = store => next => action => {
      console.log('获取到日志记录')
      const result = next.bind(store)(action);
      console.log('修改后的值为', store.getState());
      return result;
    };
    
    // redux-thunk
    const thunk = store => next => action => {
      if (typeof action === 'function') {
        action(store);
      } else {
       return next(action);
      }
    };
    const myStore = createStore(appReducer, initData, applyMiddleware([logger,thunk]));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    codesandbox: 点我查看完整代码

    传送门:
    1. redux的中间件实现思路
    2. redux的概念介绍和基础使用
    3. Redux Tool Kit(RTK)的使用

  • 相关阅读:
    FastAdmin 列表多选后批量操作数据
    移植MQTT源码到STM32F407开发板上
    深入学习Synchronized各种使用方法
    为金融而生的区块链Injective(INJ)
    Shior02(身份认证加密)
    基于SpringBoot+Vue的搬家服务系统
    jmeter线程组 bzm - Concurrency Thread Group & 阶梯式压测
    【C++】面向对象之宏观部分——类的层次结构与互相关系(has_a、use_a、is_a)
    DHCP服务的八种报文(消息)作用
    transformer代码pytorch版本,来源于哔哩哔哩的网课学习笔记
  • 原文地址:https://blog.csdn.net/qq_44621394/article/details/127800157