美文网首页
React - redux && react-redux 的使用

React - redux && react-redux 的使用

作者: 闪电西兰花 | 来源:发表于2020-08-17 17:25 被阅读0次
    • Redux 以数据存储中心 Store 为核心,修改数据、初始化数据等通过 ReducersReducers 操作完成后会通过组件对 Store 的订阅告知组件数据更新了,当然组件也可以发起对数据的修改,组件通过发送 Action,派发行为给 Store ,然后通知Reducers 进行相应操作
    • Redux 是不存在异步操作的
    • 在使用之前要先安装 Redux,当前使用版本:
    "redux": "^4.0.5"
    
    • 举个计数器例子认识下最基本的Redux ,在 src 目录下创建一个 store.js
      计数器.jpg
    // src/store.js
    
    import {createStore} from 'redux';
    
    // 初始化状态 state = 0
    // 状态的操作
    const counterReducer = (state = 0, action) => {
      switch(action.type){
        case 'add':
          return state + 1;
        case 'minus':
          return state - 1;
        default:
          return state;
      } 
    }
    
    // 创建一个store
    const store = createStore(counterReducer)
    
    export default store;
    
    • 然后来使用这个 store,写到这的时候发现页面中的加减按钮不生效,原因是组件要实现对 store 的操作,必须订阅 store 的状态更新
    // src/components/ReduxTest.js
    
    import React from 'react';
    // 引入store
    import store from '../store'
    
    class ReduxTest extends React.Component{
      render () {
        return (
          <div>
            {/* 获取状态 */}
            <p>{store.getState()}</p>
    
            {/* 通过dispatch派发action */}
            <div>
              <button onClick={() => store.dispatch({type: 'minus'})}>-</button>
              <button onClick={() => store.dispatch({type: 'add'})}>+</button>
            </div>
          </div>
        )
      }
    }
    
    export default ReduxTest
    
    • 在没有使用其他插件的情况下,我们对 index.js 添加部分内容来实现订阅,添加完成后计数器就生效了
    // index.js
    
    // 添加store.subscribe方法,在该方法中再次执行ReactDOM的渲染,实现对store的订阅
    store.subscribe(() => {
      ReactDOM.render(
        // <React.StrictMode>
          <App />,
        // </React.StrictMode>,
        document.getElementById('root')
      );
    })
    
    • 最基本的 Redux 可以看出用起来还是比较繁琐的,每个组件都要引入 storeindex.js 中还要频繁更新 DOM,为了解决这些问题可以使用相对更友好的插件库,这里安装下 react-redux,这个库主要提供了2个 api,分别是 Providerconnect,安装完成后我们来对上面的例子做重构
    "react-redux": "^7.2.0"
    
    // 1. 首先去掉index.js中的订阅
    // index.js
    
    // 添加store.subscribe方法,在该方法中再次执行ReactDOM的渲染,实现对store的订阅
    // store.subscribe(() => {
    //   ReactDOM.render(
    //     // <React.StrictMode>
    //       <App />,
    //     // </React.StrictMode>,
    //     document.getElementById('root')
    //   );
    // }) 
    
    // 2. 修改使用 ReduxTest.js 的方式
    // App.js
    
    import store from './store'
    import {Provider} from 'react-redux'
    
    {/* 上下文的形式隔代传递数据 */}
    <Provider store={store}>
      <ReduxTest></ReduxTest>
     </Provider>
    
    // 3. 重构计数器组件
    // src/components/ReactTest.js
    
    import React from 'react';
    // connect函数连接 react-redux 和 redux
    import {connect} from 'react-redux';
    
    // 映射函数
    const mapStateToProps = state => ({num: state});
    // 简化store.dispatch的操作
    const mapDispatchToProps = {
      add: () => ({type: 'add'}),
      minus: () => ({type: 'minus'})
    }
    
    // 装饰器写法:
    @connect(mapStateToProps, mapDispatchToProps)
    class ReduxTest extends React.Component{
      render () {
        const {num, add, minus} = this.props
        return (
          <div>
            {/* 获取状态 */}
            <p>{num}</p>
    
            {/* 通过dispatch派发action */}
            <div>
              <button onClick={minus}>-</button>
              <button onClick={add}>+</button>
            </div>
          </div>
        )
      }
    }
    
    export default ReduxTest
    
    • 接着再看如何让 Redux 支持异步,需要中间件的支持,在 Action 被派发至 Store 之前先通过中间件的处理,在中间件处理中可以对 Action 做很多操作,比如日志记录等,我们使用一个相对简单的中间件 redux-thunkredux-thunk 实现异步的基本原理是判断当 action 是个函数时,先执行这个函数,也同时安装一个 redux-logger 用于日志记录,然后对计数器组件做一些变化
    "redux-thunk": "^2.3.0"
    "redux-logger": "^3.0.6"
    
    // 1. 修改store.js 
    // src/store.js  修改以下部分内容,其他不变
    
    // applyMiddleware 应用中间件方法
    import {createStore, applyMiddleware} from 'redux';
    // 引入中间件
    import logger from 'redux-logger'
    import thunk from 'redux-thunk'
    
    // 创建一个store
    const store = createStore(
      counterReducer, 
      // 按照需要使用的中间件的顺序传递参数
      applyMiddleware(logger, thunk)
    )
    
    // 2. 修改计数器组件,新增异步操作
    // src/components/ReactTest.js  修改部分内容
    
    // 新增asyncAdd异步操作
    const mapDispatchToProps = {
      add: () => ({type: 'add'}),
      minus: () => ({type: 'minus'}),
      // asyncAdd方法return一个方法
      asyncAdd: () => dispatch => {
        // 异步操作
        setTimeout(()=> {
          dispatch({type: 'add'})
        },2000)
      }
    }
    
    // 添加异步操作的对应按钮
    <div>
      <button onClick={minus}>-</button>
      <button onClick={add}>+</button>
      <button onClick={asyncAdd}>async +</button>
    </div>
    
    • 完成以上的支持异步的代码之后,我们来观察不同操作的日志情况,日志记录了方法名称、操作前后的值变化等


      同步操作记录.jpg
      异步操作记录.jpg
    • 实现异步操作之后,来对 store.js 的部分做个架构分层,实际开发中可能会有多个 Store 分管不同模块的数据,在 src 目录下新建一个 store 文件夹

      store文件夹结构.jpg
    // src/store/index.js
    
    // applyMiddleware 应用中间件方法
    import {createStore} from 'redux';
    import logger from 'redux-logger'
    import thunk from 'redux-thunk'
    import {counterReducer} from './count.redux'
    
    const store = createStore(
      applyMiddleware(logger, thunk)
    );
    
    export default store;
    
    // src/store/count.redux.js
    
    export const counterReducer = (state=0, action) => {
      switch(action.type){
        case 'add':
          return state + 1;
        case 'minus':
          return state - 1;
        default:
          return state;
      }
    }
    
    // action creator
    export const add = () => ({type: 'add'})
    export const minus = () => ({type: 'minus'})
    // asyncAdd方法return一个方法
    export const asyncAdd = () => dispatch => {
      // 异步操作
      setTimeout(()=> {
        dispatch({type: 'add'})
      },1500)
    }
    
    • 修改好 store.js 的架构分层之后,下面就是使用了,接着修改在计数器组件中使用 Store 的部分
    import React from 'react';
    import {connect} from 'react-redux';
    // 将action creator导入
    import {add, minus, asyncAdd} from '../store/count.redux'
    
    const mapStateToProps = state => ({num: state});
    // count.redux之后
    const mapDispatchToProps = {add, minus, asyncAdd}
    
    @connect(mapStateToProps, mapDispatchToProps)
    class ReduxTest extends React.Component{
      render () {
        const {num, add, minus, asyncAdd} = this.props
        return (
          <div>
            {/* 获取状态 */}
            <p>{num}</p>
    
            {/* 通过dispatch派发action */}
            <div>
              <button onClick={minus}>-</button>
              <button onClick={add}>+</button>
              <button onClick={asyncAdd}>async +</button>
            </div>
          </div>
        )
      }
    }
    
    export default ReduxTest
    
    • 重构结构完成之后,继续 store 添加新的模块,实现下多个 store 如何使用
    // src/store/index.js
    
    // 引入combineReducers
    import {createStore, applyMiddleware, combineReducers} from 'redux';
    
    const store = createStore(
      // combineReducers将多个模块的reducer合并
      // 在使用具体某个reducer的时候要注意命名空间
      combineReducers({counter: counterReducer}), 
      applyMiddleware(logger, thunk)
    );
    
    • 关于 redux 的原理可以了解下,使用 redux 的时候通常先通过调用 createStore 方法初始化一个 store,如下:
    const store = createStore(
      // combineReducers将多个模块的reducer合并
      // 在使用具体某个reducer的时候要注意命名空间
      combineReducers({user}),
      applyMiddleware(logger, thunk)
    );
    
    1. createStore 接收2个参数,一个是 reducer,当有多个 reducer 的时候,使用 combineReducers,另一个我们叫 enhancer,可以理解为强化器
    2. 在上面的代码使用中,这里的 enhancer 就是 applyMiddleware 中间件函数,enhancer存在的时候会首先用于强化我们的 store
    export function createStore(reducer, enhancer) {
      if(enhancer) {
        return enhancer(createStore)(reducer)
      }
    }
    
    1. store 的基本 api 分别是:getStatesubscribedispatchgetState 的原理其实就是 return currenState 返回当前状态值,subscribe 主要用于组件对 store 的监听,dispatch 用于执行 reducer ,根据 action 的具体指令执行对应的方法对状态进行更新,然后返回全新的状态,执行完毕后通知所有监听器执行更新
    export function createStore(reducer, enhancer) {
      if(enhancer) {
        return enhancer(createStore)(reducer)
      }
    
      // 当前状态值、监听器(监听store变化并执行的回调函数)
      let currentState = []
      let currentListeners = []
    
      function getState () {
        return currentState
      }
    
      function subscribe (listener) {
        currentListeners.push(listener)
      }
    
      function dispatch (action) {
        currentState = reducer(currentState, action)
        currentListeners.forEach(...)
        return action
      }
    
      return {getState, subscribe, dispatch}
    }
    
    1. applyMiddleware 接收若干个中间件,并且是按顺序接收的,然后中间件是希望放在 Actionreducer 之前执行的,通过一些方法将若干个中间件组成一个中间件链
    export function applyMiddleware (...middlewares) {
      return createStore => (...args) => {
        const store = createStore(...args)
        let dispatch = store.dispatch
    
        // 当前基本api都先赋值给中间件
        const midApi = {
          getState: store.getState,
          dispatch: (...args) => dispatch(...args)
        }
    
        // 组成中间件链
        const middlewareChain = middlewares.map(middleware => middleware(midApi))
        // compose方法可以将以方法组成的数组变成函数层层嵌套,也就是函数复合
        diapatch = compose(...middlewareChain)(store.dispatch)
        return {
          ...store,
          dispatch
        }
      }
    }
    
    • react-redux 的原理主要涉及到新增的2个 api,分别是 connectprovider
    1. connect 主要用于将 stateaction 相关内容绑定至组件,原理上主要是使用了上下文 contextstate 进行隔代传递
    2. provider 做的主要就是上下文提供,将 store 传给需要的组件

    相关文章

      网友评论

          本文标题:React - redux && react-redux 的使用

          本文链接:https://www.haomeiwen.com/subject/cdqbrktx.html