乐趣区

关于前端:redux-源码解析-从零实现一个-redux

1. Redux 简略介绍

Redux 是 JavaScript 利用状态容器,提供可预测化的状态治理。

能够让你构建统一化的利用,运行于不同的环境(客户端、服务器、原生利用),并且易于测试。不仅于此,它还提供 超爽的开发体验,比方有一个工夫旅行调试器能够编辑后实时预览。

2. 为什么要用 redux,什么状况下须要用到 redux

已经有人说过这样一句话。

“ 如果你不晓得是否须要 Redux,那就是不须要它。”

Redux 的创造者 Dan Abramov 又补充了一句。

“ 只有遇到 React 切实解决不了的问题,你才须要 Redux。”

如果一个利用不是很简单,能够通过 react 的外部 state 就满足对数据的治理,那就没必要应用 redux。

因为应用 redux 每次写代码会额定减少代码的编写以及可能须要多创立多几个文件。

那什么时候适宜用 redux?

  1. 组件树宏大,不同的组件节点须要共享状态
  2. 某个组件须要去批改全局的状态或批改其余组件的状态
  3. 与后端服务器有比拟多的数据交互,并且组件视图依赖后端返回的数据

应用 redux 带来什么益处?

  1. redux 让 state 的变动变得可预测,因为批改 state 只能通过 dispatch 一个 action,这个过程是可监控的,在开发环境中,联合 redux-devtools 还能够实现工夫旅行、录制、重放等
  2. redux 将 state 对立治理,会使代码更加有法则,易于保护治理。

3. redux 基础知识

3.1 redux API

  • 顶级裸露的 API

    • [createStore(reducer, [preloadedState], [enhancer])](https://redux.js.org/api/crea…
    • combineReducers(reducers)
    • applyMiddleware(…middlewares)
    • bindActionCreators(actionCreators, dispatch)
    • compose(…functions)
  • Store API

    • getState()
    • dispatch(action)
    • subscribe(listener)
    • replaceReducer(nextReducer))

3.1.1 createStore

createStore 用于创立一个 store 实例,创立时,第一个参数会传入一个 reducer 函数,store 保留了 state 数据,传入的 reducer 函数定义了批改 state 的规定。

3.1.2 store.getState

store.getSate() 返回 store 外部的 state 数据,state 数据在内部无奈间接拜访,必须通过 getState 办法获取

3.1.3 store.dispatch

store.dispatch() 派发一个 actionaction 是一个对象,个别至多蕴含一个 type 字段,例如:{type: 'add'},除了 type 字段外,也能够蕴含其余数据,type 用于匹配 reducer 中的批改规定,其余数据用于更新 state。

想要批改 state 中的数据,必须通过 dispatch

3.1.4 store.subscribe

store.subscribe 用于订阅 store 的数据变动,监听函数会在 dispatch 调用时执行

3.2 redux flow

redux 与 react 联合应用的数据流:

  1. 在 react 组件中调用 dispatch(action),派发一个 actionaction 是一个形容“产生了什么”的一般对象,例如:{type: 'add', value: 1},这个 action 对象能够了解为“减少数值 1”
  2. store 取到 action 后,调用构建 store 传入到 reducer 函数,并且传入以后的 stateaction
  3. reducer 取到 stateaction 后,会通过 action.type 匹配到批改 state 的规定,而后批改并返回新的 state
  4. store 保留新的 state,而后所有订阅了 store.subscribe() 的监听器都被调用,并通过 store.getState() 获取新的 state,从新渲染组件。

4. redux 应用

4.1 reducer 介绍

创立 store 的时候,须要传入一个 reducer 函数。

reducer = (state, action) => {}

Reducer (也称为 reducing function) 函数承受两个参数:

  • state: 之前累积运算的后果和以后被累积的值
  • action: 形容了产生什么事件的一个对象,个别带有 type 字段。例如:{type: 'ADD'}

返回的是一个新的累积后果(state)。

Reducers 指定了利用状态的变动如何响应 actions 并发送到 store 的,记住 actions 只是形容了 有事件产生了 这一事实,并没有形容利用如何更新 state。

reducer 是一个纯函数,即接管的参数不变的状况下,返回值也不会发生变化。

永远不要 在 reducer 里做这些操作:

  • 批改传入参数;
  • 执行有副作用的操作,如 API 申请和路由跳转;
  • 调用非纯函数,如 Date.now()Math.random()

reducer 肯定要放弃污浊。只有传入参数雷同,返回计算失去的下一个 state 就肯定雷同。没有非凡状况、没有副作用,没有 API 申请、没有变量批改,单纯执行计算。

4.2 创立一个 store

装置 redux:npm install reduxyarn add redux

src/store/index.js

import {createStore} from 'redux';
// 定义 reducer 函数
const reducer = (state = 0, action) => {switch (action.type) {
    case 'add':
      return state + 1;
    case 'minus':
      return state - 1;
    default:
      return state;
  }
};
// 应用 createStore,传入 reducer 函数,生成一个 store
const store = createStore(reducer);

export default store;

4.3 应用 store

src/pages/ReduxPage.js

import React, {Component} from 'react';
import store from '../store';

export default class ReduxPage extends Component {componentDidMount() {
    // 到目前为止,通过 dispath 一个 action 批改 state 后,页面并不会自动更新
    // 在没有应用其余库配合前,临时应用这种形式更新界面,订阅 store 变动,而后强制更新页面
    this.unsubscribe = store.subscribe(() => {this.forceUpdate();
    });
  }

  add = () => {store.dispatch({ type: 'ADD'});
  };

  minus = () => {store.dispatch({ type: 'MINUS'});
  };

  render() {
    return (
      <div>
        ReduxPage
        <div>count: {store.getState()}</div>
        <button onClick={this.add}>add</button>
        <button onClick={this.minus}>minus</button>
        <button onClick={() => this.unsubscribe()}>unsubscribe</button>
      </div>
    );
  }
}

src/App.js

import React from 'react';
import ReduxPage from './page/ReduxPage';

function App() {
  return (
    <div className="App">
      <ReduxPage />
    </div>
  );
}

export default App;

到此就曾经能够简略的应用 redux 进行状态治理

5. redux 实现

5.1 createStore 实现

创立 redux 并裸露 createStore 办法

src/redux/index.js

import createStore from './createStore'

export {createStore}

5.1.1 实现 createStore

createStore 创立了一个 store 实例,这个实例蕴含了 getStatesubscribedispatch 等办法

export default function createStore(reducer) {function getSate() {}
  function subscribe() {}
  function dispatch() {}

  return {
    getSate,
    subscribe,
    dispatch,
  }
}

5.1.2 实现 getState 办法

getState 只须要返回以后等 state

export default function createStore(reducer) {
  // 定义 currentState,保留以后的 state
  let currentState;

  function getSate() {
    // 调用 getState 时,返回以后的 state
    return currentState;
  }
  function subscribe() {}
  function dispatch() {}

  return {
    getSate,
    subscribe,
    dispatch,
  }
}

5.1.3 实现 subscribe 办法

subscribe,将订阅的回调函数保留到回调函数数组中

export default function createStore(reducer) {
  let currentState;
  // 定义 currentListeners,保留订阅的回调函数
  let lisenters = [];

  function getSate() {return currentState;}
  function subscribe(listener) {
    // 将回调函数保留到回调函数数组中
    lisenters.push(listener)
    // 返回一个勾销订阅的函数
    return function unsubscribe() {const index = lisenters.indexOf(lisenter);
      lisenters.splice(index, 1);
    }
  }
  function dispatch() {}

  return {
    getSate,
    subscribe,
    dispatch,
  }
}

5.1.4 实现 dispatch 办法

dispatch 一个 action(dispatch(action))是批改 state 的惟一办法。

dispatch 办法会调用创立 store 时传入的 reducer 函数,并把以后的 state 和 action 传入。

最初会循环调用 subscribe 办法收集的回调函数。

export default function createStore(reducer) {
  let currentState;
  let lisenters = [];

  function getSate() {return currentState;}

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

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

  function dispatch(action) {
    // 调用 reducer 函数,批改以后的 state
    currentState = reducer(currentState, action)
    // 循环调用回调函数
    for (let i = 0; i < lisenters.length; i++) {const lisenter = lisenters[i];
      lisenter()}
  }

  return {
    getSate,
    subscribe,
    dispatch,
  }
}

5.1.5 初始化 state 操作

export default function createStore(reducer) {
  let currentState;
  let lisenters = [];

  function getState() {/* 略 */}

  function subscribe(listener) {/* 略 */}

  function dispatch(action) {/* 略 */}

  // 执行一次初始化的 dispatch,这样能够保障整个 state 树都领有初始状态值,这样在定义 reducer 函数时定义都初始 state 才会失效。dispatch({type: '@@redux/INIT'});

  return {
    getState,
    subscribe,
    dispatch,
  };
}

到这里,曾经实现了 redux 的根底性能,除此之外,上述的几个办法中,还须要做一些参数兼容性解决,边缘状况的解决等。

此时能够批改下面的例子中的代码,把 redux 改为本人实现的 redux(src/redux/index.js),批改后性能失常应用。

目前这里 action 还只能反对 js 的一般对象(plain object),action 不反对传入一个函数(异步)。如果要实现异步操作,要传入一个值为函数的 action 还须要借助中间件。

如何实现异步操作?

到目前为止,应用 redux 还只能应用一般对象作为 action,如果须要异步的更新 state,例如实现动静从后端获取数据,而后批改 state,还无奈实现。

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'});
  };
    // 传入 action 为函数,进行异步操作
  asyncAdd = () => {store.dispatch(() => {setTimeout(() => {store.dispatch({ type: 'ADD'});
      });
    });
  };

  render() {
    return (
      <div>
        <div>count: {store.getState()}</div>
        <button onClick={this.add}>add</button>
        <button onClick={this.minus}>minus</button>
        <button onClick={this.asyncAdd}>asyncAdd</button>
      </div>
    );
  }
}

要实现异步操作,须要接入 redux 中间件

6. redux 中间件的应用

Redux 只是纯正的状态管理器,默认只反对同步,实现异步工作比方提早、网络申请等,须要中间件的反对

store 是 redux 的核心内容,除了 store 相干都内容,redux 还提供了其余一些 api,用于扩大、接入其余库,实现更弱小的性能。其中 applyMiddlewares 用于中间件的接入利用。

中间件的执行在调用 dispatch 到最终 reducer 函数批改 state 的这个过程之间。

接下来试用一下最简略的 thunk 和 logger 中间件

src/store/index.js

import {createStore, applyMiddleware} from 'redux';
// import {createStore} from '../redux';

import thunk from 'redux-thunk';
import logger from 'redux-logger';

const reducer = function (state = 0, action) {switch (action.type) {
    case 'ADD':
      return state + 1;
    case 'MINUS':
      return state - 1;
    default:
      return state;
  }
};

// 应用 applyMiddleware 接入 logger,thunk 中间件
const store = createStore(reducer, applyMiddleware(logger, thunk));

export default store;

中间件是依照肯定的程序执行的,调整中间件的地位,可能会失去不同的执行后果。

例如下面例子,logger 放在 thunk 后面,能够打印到异步的 action 的日子,如果 logger 放到 thunk 前面,就无奈打印出异步的 action 的信息,因为 thunk 的逻辑代码中,如果 action 是函数,就会执行该函数,间接跳过后续中间件。这个在理论开发中可能要理解并留神一下。

7. redux 中间件实现

实际上中间件是对原始的 dispatch 做了加强、扩大,在 dispatch 里面又包了一层函数,执行中间件自身的性能,执行完中间件自身的性能后,再去调用原始的 dispatch。

中间件可能是一个,也可能是无数个,要保障这些中间件都能无效并且有序的执行,就须要一个好的办法将中间件串联起来。

7.1 实现 compose 函数

compose 函数实现将中间件串起来,一个接一个执行,并将上一个中间件的执行后果,传过下个中间件。

实现这样的成果 compose(f1, f2, f3)(...args) => f1(f2(f3(...args)))

compose 的实现应用到 Array.prototype.reduce() 办法。

src/redux/compose.js

export default function compose(...funcs) {
  // funcs 长度为 0 的话,返回一个默认的函数
  if(funcs.length === 0) {return arg => arg}
  // funcs 长度为 1 的话,间接返回该函数
  if(funcs.length === 1) {return funcs[0]
  }
  // 应用 reduce 将函数串联起来
  return funcs.reduce((a,b) => (...args) => a(b(...args)))
  // 上面是将下面这行代码的箭头函数拆开,不便了解,要想了解这段代码,必须先了解 reduce 的应用。// 例如:funcs 是 [logger,thunk],则最终会失去的是 logger(thunk(..args))
  // return funcs.reduce(function(a, b) {//      return function(...args) {//     return a(b(...args))
  //   }
  // })
}

7.2 实现 applyMiddleware 函数

applyMiddleware 函数传入中间件,并返回一个加强函数,该函数会对 createStore 函数进行加强。

src/redux/applyMiddleware.js

import compose from './compose';

export default function applyMiddleware(...middlewares) {return (createStore) => (reducer) => {let store = createStore(reducer);
    let dispatch = () => {
      throw new Error('不容许在构建中间件时调用 dispatch,其余中间件不会利用此 dispatch。');
    };
    // 提供给中间件到 API
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (action, ...args) => dispatch(action, ...args),
    };
    // 调用中间件函数(构建),并返回一个调用后到数据
    const chain = middlewares.map((middleware) => middleware(middlewareAPI));
    // 应用 compose 将中间件数组串起来
    dispatch = compose(...chain)(store.dispatch);

    return {
      ...store,
      dispatch,
    };
  };
}

这里 let dispatch 定义了一个函数,最初才给 dispatch 赋值为“加强”后的 dispatch,是为了避免在构建中间件的时候就调用 dispatch。

src/redux/createStore.js

批改 createStore 函数减少一个 enhancer 参数,用于加强 createStore

export default function createStore(reducer, enhancer) {if (typeof enhancer !== 'undefined') {if (typeof enhancer !== 'function') {throw new Error('enhancer 必须是一个函数');
    }
    return enhancer(createStore)(reducer);
  }
  /* 略 */
}

当应用 applyMiddleware 函数接入中间件的时候减少 store 性能的时候,enhancer 就是 applyMiddleware

7.3 实现中间件

中间件一共 3 层函数,

第一层函数,是中间件的构建函数,构建时会传入 getState 和 dispatch 办法,中间件的初始化也会在这里执行,使中间件在执行时能够用到这两个办法。

第二层函数,次要用于将中间件串连起来

第三层函数,是该中间件要扩大的性能的次要逻辑代码实现

7.3.1 实现 redux-thunk

// 构建中间件的时候,传入 getState, dispatch,使中间件能够用这两个办法
const thunk = ({getState, dispatch}) => {
  // 第二层是 compose 的时候生成的一层包一层的函数,其中 next 就是下一层中间件,最初一个 next 就是原始的 dispatch
  return (next) => {
    // 中间件次要逻辑代码
    return (action) => {if (typeof action === 'function') {return action(dispatch, getState);
      }
      return next(action);
    };
  };
};

8. react-redux

单纯依附 redux 的话,只能通过 subscribe 订阅数据的变动,手动更新界面,并且每次都须要通过 getState 获取最新的数据。

这样操作起来比拟麻烦。

要更好的将 react 跟 redux 联合起来,就须要借助像 react-redux 这样的库。

8.1 react-redux api

react-redux 提供了两个 api:Providerconnect

API

  • <Provider store>
  • connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
  1. Provider 使组件层级中的 connect() 办法都可能取得 Redux store,失常状况下,你的根组件应该嵌套在 <Provider> 中能力应用 connect() 办法。
  2. connect 顾名思义,就是连贯 Redux store 与组件。它是一个高阶组件(HOC),传入一个组件,并且返回一个新的组件,扩大原来的组件使原来组件能够获取到 sotre 中的数据与变更数据的办法。连贯操作不会扭转原来的组件类。而是 返回 一个新的已与 Redux store 连贯的组件类。

8.2 react-redux 应用

将下面例子改成应用 react-redux

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

import {Provider} from './react-redux';
import store from './store'

// 应用 Provider 为后辈组件提供 store,使组件层级中的 connect() 办法都可能取得 Redux store
ReactDOM.render(<Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

src/page/ReactReduxPage.js

import React, {Component} from 'react';
import {connect} from '../react-redux';

class ReactReduxPage extends Component {add = () => {this.props.dispatch({ type: 'ADD'});
  };

  minus = () => {this.props.dispatch({ type: 'MINUS'});
  };

  asyncAdd = () => {this.props.dispatch((dispatch) => {setTimeout(() => {dispatch({ type: 'ADD'});
      }, 1000);
    });
  };

  render() {
    return (
      <div>
        <div>count: {this.props.count}</div>
        {/* <button onClick={this.add}>add</button> */}
        <button onClick={this.props.add}>add</button>
        <button onClick={this.minus}>minus</button>
        <button onClick={this.asyncAdd}>asyncAdd</button>
      </div>
    );
  }
}

// mapStateToProps 用于将 redux 的 state 传给 组件
// mapDispatchToProps 用于将 dispatch 与 action 封装成办法,再传给组件,不便组件里批改 state,而不必写大量 dispatch(xxxx)
const mapStateToProps = (state) => ({count: state});
// mapDispatchToProps 传一个对象的应用形式
const mapDispatchToProps = {add: () => ({type: 'ADD'}),
};

// mapDispatchToProps 传一个函数的应用形式,上面代码与下面传对象的形式实现同样的性能
// const mapDispatchToProps = (dispatch) => {
//   return {
//     dispatch,
//     add: () => dispatch({ type: 'ADD'}),
//   };
// };

// 应用 connect 办法连贯组件与 store
// 传入的 mapStateToProps 和 mapDispatchToProps 定义了须要传递给组件的 state 与 dispatch
export default connect(mapStateToProps, mapDispatchToProps)(ReactReduxPage);

8.3 react-redux 中用到的 react 的 api:Context

这里 Context 用于将 redux 中的 state 传递到各个组件,使组件能够不便的应用 redux 的 state 中的数据。

在一个典型的 React 利用中,数据是通过 props 属性自上而下(由父及子)进行传递的,但这种做法对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI 主题),这些属性是应用程序中许多组件都须要的。Context 提供了一种在组件之间共享此类值的形式,而不用显式地通过组件树的逐层传递 props。

Context 在日常开发中用的比拟少,在第三方库中用到比拟多。react-redux 中就用到这个性能。

Context 的应用

// Context 能够让咱们毋庸明确地传遍每一个组件,就能将值深刻传递进组件树。// 为以后的 theme 创立一个 context(“light”为默认值)。const ThemeContext = React.createContext('light');

class App extends React.Component {render() {
    // 应用一个 Provider 来将以后的 theme 传递给以下的组件树。// 无论多深,任何组件都能读取这个值。// 在这个例子中,咱们将“dark”作为以后的值传递上来。return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

// 两头的组件再也不用指明往下传递 theme 了。function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

class ThemedButton extends React.Component {
  // 指定 contextType 读取以后的 theme context。// React 会往上找到最近的 theme Provider,而后应用它的值。// 在这个例子中,以后的 theme 值为“dark”。static contextType = ThemeContext;
  render() {return <Button theme={this.context} />;
  }
}

function Button({theme}) {return <button style={{ backgroundColor: theme}}>Test Button</button>;
}

// 也能够应用 Consumer 获取 context
// function ThemedButton () {
//   return (
//     <ThemeContext.Consumer>
//       {value => <Button theme={value} />}
//     </ThemeContext.Consumer>
//   )
// }

// 或者应用 hooks,useContext()

8.4 react-redux 中用到的 redux 的 api:bindActionCreators

bindActionCreators 在 connect 时传 mapDispatchToProps 为对象时会用到,用于将对象中每个 key 对应的 value(action creator)绑定一层 dispatch 调用。而后通过 props 传给组件,使组件中能够更不便的批改 redux 的 state,缩小组件中写很多 dispatch(xxx)。
action creator 前面会讲。
用法:bindActionCreators(actionCreators, dispatch)

bindActionCreators 把一个 value 为不同 action creator 的对象,转成领有同名 key 的对象。同时应用 dispatch对每个 action creator 进行包装,以便能够间接调用它们。

作用大略是上面这样的成果:

{add: () => ({type: 'ADD'}),
};
// 变成
{add: () => dispatch({type: 'ADD'})
}

参数:

  1. actionCreators (Function or Object): 一个 action creator,或者一个 value 是 action creator 的对象。
  2. dispatch (Function): 一个由 Store 实例提供的 dispatch 函数。

8.4.1 Action Creator

Action Creator 是指用来生成 action 的函数

例如:

function ADD_TODO(text) {return { type: 'ADD', text}
}
dispatch(ADD_TODO('待办事项 1')) // dispatch({type: 'ADD', text: '待办事项 1'})

上例中 ADD_TODO 用来生成 Action {type: 'ADD', text}ADD_TODO 就是一个 action creator 函数

Action 是一个信息的负载,而 action creator 是一个创立 action 的工厂。

8.4.1 bindActionCreators 实现

作用:在 mapDispatchToProps 为对象时,使对象 {add: () => ({type: 'ADD'})} 相当于 add = () => dispatch({type: 'ADD'})

// bindActionCreator 会对 action creator 进行包装,加上 dispatch 调用
function bindActionCreator(actionCreator, dispatch) {return (...args) => dispatch(actionCreator(...args));
}

export default function bindActionCreators(actionCreators, dispatch) {if (typeof actionCreators === 'function') {return bindActionCreator(actionCreators, dispatch)
  }
  const boundActionCreators = {};
  for (let key in actionCreators) {const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
    }
  }
  return boundActionCreators;
}

8.4 react-redux 实现

src/react-redux/index.js

import Provider from './Provider';
import connect from './connect';
// 裸露 Provider,connect API
export {Provider, connect};

创立一个 Context,用于数据通信

src/react-redux/Context.js

import React from 'react';

export const ReactReduxContext = React.createContext(null);

export default ReactReduxContext;

8.5 Provider 的实现

import React, {Component} from 'react';
import {ReactReduxContext} from './Context';

export default class Provider extends Component {render() {
    // 应用 Context.Provider 为组件提供 store
    return (<ReactReduxContext.Provider value={this.props.store}>
        {this.props.children}
      </ReactReduxContext.Provider>
    );
  }
}

8.6 connect 的实现

connect 承受 mapStateToProps 和 mapDispatchToProps 参数,返回一个高阶组件。

“连贯”须要应用 store 的组件,把 store 中的值传递给组件

import React, {useLayoutEffect, useReducer, useContext} from 'react';
// import {bindActionCreators} from 'redux';
import {bindActionCreators} from '../redux';
import {ReactReduxContext} from './Context';

const connect = (mapStateToProps = (state) => state, mapDispatchToProps) => (WrappedComponent) => (props) => {const store = useContext(ReactReduxContext);
  const {getState, dispatch, subscribe} = store;

  // mapStateToProps 是在应用 connect 时传入的第一个参数,// getState 获取到 state 到值,而后传递给 mapStateToProps 应用
  // mapStateToProps 执行实现后返回须要传递给组件的 stateProps
  const stateProps = mapStateToProps(getState());

  // connect 的第二个参数 mapDispatchToProps 能够是对象或者函数
  let dispatchProps;
  if (typeof mapDispatchToProps === 'object') {
    // 如果 mapDispatchToProps 是一个对象,则应用 bindActionCreators 将该对象包装成能够间接调用的函数对象
    dispatchProps = bindActionCreators(mapDispatchToProps, dispatch);
  } else if (typeof mapDispatchToProps === 'function') {
    // 如果 mapDispatchToProps 是一个函数,则调用函数并传入 dispatch
    dispatchProps = mapDispatchToProps(dispatch);
  } else {
    // 默认传递 dispatch
    dispatchProps = {dispatch};
  }
    
  function storeStateUpdatesReducer(state, action) {return state + 1;}
  // 应用 useReducer 实现强制更新页面
  // useReducer 返回的数组蕴含两个项 [state, dispatch],调用 dispatch 返回新的 state 时,组件会从新渲染
  const [, forceComponentUpdateDispatch] = useReducer(
    storeStateUpdatesReducer,
    0
  );

  useLayoutEffect(() => {
    // 订阅 store 中数据更新,强制刷新页面
    const ubsubscribe = subscribe(() => {forceComponentUpdateDispatch({ type: 'STORE_UPDATED'});
    });
    return () => {
      // 卸载组件勾销订阅
      ubsubscribe && ubsubscribe();};
  }, [store]);

  // 将须要“连贯”的组件返回,并传递给组件须要的数据
  return <WrappedComponent {...props} {...stateProps} {...dispatchProps} />;
};

export default connect;

9. redux devtools

9.1 接入 redux

import {createStore, applyMiddleware} from 'redux';
// 引入 composeWithDevTools
import {composeWithDevTools} from 'redux-devtools-extension';

import thunk from 'redux-thunk';
import logger from 'redux-logger';

const reducer = function (state = 0, action) {switch (action.type) {
    case 'ADD':
      return state + 1;
    case 'MINUS':
      return state - 1;
    default:
      return state;
  }
};

const store = createStore(
  reducer,
  composeWithDevTools(applyMiddleware(logger, thunk))
);
export default store;

9.2 chrome 插件:redux devtools

  1. 关上 Chrome 调试工具,抉择 redux 进入 redux devtools 调试界面。
  2. 抉择应用的性能:Log monitorInspectorChart,默认是 Inspector
  3. 录制的 redux 执行过程中执行的 action 的列表,点击每个 action 能够进入到该 action 下的具体状态
  4. Action 能够查看以后 action 的具体内容

  5. State 能够查看以后 state 数据的具体内容

  6. Diff 能够查看以后 action 执行后,state 产生了哪些变动

  7. Trace 跟踪以后 action 执行的代码地位

  8. Test 测试模版


  9. Start recording/Pause recording 开始 / 进行录制,能够指定从什么时候开始录制到什么进行。
  10. Lock changes 锁定以后的录制状态,再有 action 执行也不会改变以后录制的状态。
  11. Dispatcher 用于派发 action,Dispatcher 框内能够编辑 action 的内容。
  12. Slider 用于自动播放整个录制的过程。
  13. Import/ Export 导入 / 导出,导出以后录制的状态的 JSON 文件,之后用于导入,导入后会还原到该文件保留到状态。
  14. Settings 是 redux devtools 的配置菜单。
  15. 切换性能面板到 Log monitor 能够查看整个过程的日志

  16. 切换性能面板到 Chart 能够查看整个 store 的状态

扩大浏览:

官网:https://redux.js.org/

github: https://github.com/reduxjs/redux

中文文档:https://www.redux.org.cn/

React Context:https://zh-hans.reactjs.org/d…

你可能不须要 redux:You Might Not Need Redux

首发于:https://www.silinchen.com

退出移动版