redux & react-redux相关知识整理

220 阅读8分钟
  • Redux

    • 初识redux

      ​ redux在官网上的定义是A Predictable State Container for JS Apps。中文文档上解释为 Redux是Javascript状态容器,提供可预测化的状态管理。

      ​ 在react中,数据在组件中是单向流动的,通过props从父组件流向子组件,当非父子组件之间进行通信就比较麻烦,,redux,mobx,RxJS等都是一些常用的方法,但是为了避免进行造轮子,如果你的项目中用不到状态管理,能不用redux就就不用redux,除此之外,react hooks是一个很好地选择。对于react hooks的一些信息和理解,我在之前也大致上说过一些,有兴趣的话可以去了解一下,mobx后期会继续跟进,本文主要讲述redux的相关内容,我们继续往下看。React-Hooks传送门: 点这里

    • redux设计理念和原则

      src=http___www.pianshen.com_images_401_568b9babb2be65845dda416c5d4fae81.png&refer=http___www.pianshen.jpeg

      • redux设计理念

        ​ 如图所示,redux是将整个应用状态存储到一个地方称为store,里面保存着一个状态的store tree,组件可以派发(dispatch)行为(action)给store,而不是直接通知其他组件,组件内部通过订阅store中的状态state来刷新自己的视图。

      • redux三大原则

        • 单一数据源

          整个应用的state被存储在一棵store tree中,并且这个tree只存在于唯一一个store中。

        • state是只读的

          唯一改变state的方法就是触发action,action是用于描述一个已发生事件的普通对象

        • 使用纯函数来执行修改

          为了描述action如何改变state,你需要编写reducers

    • redux中主要的api

      WechatIMG99.png

      • redux源码比较简单,源码以及注释加起来也就700行的样子,有兴趣的同学可以去研究研究,基本上花个半天时间差不多就明白了,redux暴露出来的主要的api有createStore,combineReducers,bindActionCreators,applyMiddleware,compose这些。

      • createStore(reducer,[preloadedState],enhancer)

        • 作用

          创建一个Redux store来存放应用中所有的state,应用中应有且仅有一个store

        • 参数

          • reducer(Function): 接收两个参数,分别是当前的state和要处理的action,返回新的state树
          • [preloadedState](any):初始时的state,如果你使用combineReducers创建reducer,它必须是一个普通对象,与传入的keys保持同样的结构,否则,你可以自由传入任何reducer可理解的内容
          • enhancer(Function):一个组合store creator的高阶函数,返回一个新的强化过的store creator,这与middleware相似。它允许你通过复合函数改变store接口
        • 返回值

          Store:保存了应用所有state的对象,改变state唯一的方法就是dispatch action,也可以subscribe监听state的变化,然后更新UI

      • combineReducers()

        • 作用
          • combineReducers所做的只是生成一个函数,这个函数用来调用一系列的reducer,每个reducer根据它们的key来筛选state中的一部分数据并处理,然后这个生成的函数再将所有reducer的结果合并成一个大的对象
          • combinerReducers中包含所有的reducer都没有更改state,那么也就不会创建一个新的对象
      • applyMiddleware(...middlewares)

        • 作用

          使用包含自定义功能的middleware来扩展redux功能。Middleware 可以让你包装 store 的 dispatch 方法来达到你想要的目的。同时, middleware 还拥有“可组合”这一关键特性。多个 middleware 可以被组合到一起使用,形成 middleware 链。其中,每个 middleware 都不需要关心链中它前后的 middleware 的任何信息。 Middleware 最常见的使用场景是无需引用大量代码或依赖类似 Rx 的第三方库实现异步 actions。这种方式可以让你像 dispatch 一般的 actions 那样 dispatch 异步 actions。

      • bindActionCreators(actionCreators,dispatch)

        • 作用
          • 将单个活多个actionCreator转化为dispatch(action)的函数集合形式,开发者不用再手动dispatch(actionCreators)
      • compose(...functions)

        • 作用
          • 从右到左组合多个函数,这是函数式编程中的方法,为了方便,被放倒了redux中,当需要把多个store enhancer依次执行的时候,需要用到它
    • redux的用法

      • store.js
         import {createStore,applyMiddleware} from './reducers'
         import rootReducers from './reducers'
        
         //处理异步
         import thunk from 'redux-thunk'
        
         // createStore的第一个参数必须是一个reducer,如果是多个,请在reducers目录下先使用combineReducers全并之后再导出
        
         exoirt default createStore(rootReducers,  applyMiddleware(thunk)
         )
        
      • reducers
          //index.js 合并多个reducer并到处
          import { combineReducers } from 'redux'
          import reducer1 from './reducer1'
          import reducer2 from './reducer2'
          export default combineReducers({reducer1,reducer2})
        
        
          //reducer1.js 纯函数 接收state和action并返回新的state
          const initState = [{
            id: 1,
            title: 'Apple',
            price: 8888.66,
            amount: 10
          }, {
            id: 2,
            title: 'Orange',
            price: 4444.66,
            amount: 12
          }]
          export default (state = initState,action) => {
            switch(action.type) {
              case 'increment':
                return state.map(item => {
                  if(item.id ===action.payload.id) {
                    item.amount += 1
                  }
                  return item
                })
              case 'decrement:
                return state.map(item => {
                  if (item.id === action.payload.id) {
                    item.amount -= 1
                  }
                  return item
                })
              default:
                return state               
            }
          }
        
      • action
        //actionType.js
          export default {
            INCREMENT: 'INCREMENT',
            DECREMENT: 'DECREMENT'
          }
        
         //index.js
          import actionType from './actionType' 定义action的行为 
          // action有两种写法
        
          // 第一种写成一个对象,这是标准的action, 但是,问题是不方便传递动态参数
          // export const increment = {
          //   type: actionType.CART_AMOUNT_INCREMENT,
          //   payload: {
          //      id: 123
          //  }
          // }
        
          // 在工作中,常用的一种方式是使用actionCreator, 它是一个方法,返回一个对象,这个对象才是真正的action
          export const increment = (id) => {
            return {
              type: actionType.CART_AMOUNT_INCREMENT,
              payload: {
                id
              }
            }
          }
          export const increment  = (id) => {
            return {
              type:actionType.INCREMENT,
              payload:{
                id
              }
            }
          }
        
          export const decrement = (id) => {
            return {
              type:actionType.DECREMENT,
              payload:{
                id
              }
            }
          }
          // 异步action, 使用redux-thunk 之后,就可以在actionCreator里return一个方法,这个方法的参数是dispatch
          export const decrementAsync = id => dispatch => {
            setTimeout(() => {
              dispatch(decrement(id))
            }, 3000)
          }
        
      • index.js //入口
          import React from 'react
          import {render} from 'react-dom'
          import App from './App'
          import store from './store'
          render(
            <App store={store} />,
            document.querySelector('#root)
          )
        
      • App.js
         import React, { Component } from 'react'
         import {
           List
         } from './components'
        
         export default class App extends Component {
           render() {
             return (
               <div>
                 <List store={this.props.store} />
               </div>
             )
           }
         }
        
      • List.js
          import React, { Component } from 'react'
          import { increment, decrement ,decrementAsync} from '../../actions'
          export default class List extends Component {
            constructor () {
              super()
              this.state = {
                list: []
              }
            }
        
            getState = () => {
              this.setState({
                list: this.props.store.getState().cart
              })
            }
        
            componentDidMount () {
              this.getState()
              this.props.store.subscribe(this.getState)
            }
            render() {
              console.log(this.state)
              return (
                <table>
                  <thead>
                    <tr>
                      <th>id</th> 
                      <th>商品名称</th>
                      <th>价格</th>
                      <th>数量</th>
                      <th>操作</th>
                    </tr>
                  </thead>
                    <tbody>
                      {
                        this.state.list.map(item => {
                          return (
                            <tr key={item.id}>
                              <td>{item.id}</td>
                              <td>{item.title}</td>
                              <td>{item.price}</td>
                              <td>
                              <button onClick={this.props.decrementAsync.bind(this, item.id)}>延时三秒减1</button>
                                <button onClick={
                                  () => {
                                    this.props.store.dispatch(decrement(item.id))
                                  }
                                }>-</button>
                                <span>{item.amount}</span>
                                <button onClick={
                                  () => {
                                    this.props.store.dispatch(increment(item.id))
                                  }
                                }>+</button>
                              </td>
                              <td></td>
                            </tr>
                          )
                        })
                      }
                    </tbody>
                  </table>
                )
              }
            }
        
  • react-redux

    • 简介

      React-Redux 是 Redux 的官方 React 绑定库。它能够使你的 React 组件从 Redux store 中读取数据,并且向 store 分发 actions 以更新数据
    • react-redux api

      • Provider
        • 作用
          • Provider 使组件层级中的 connect() 方法都能够获得 Redux store。正常情况下,你的根组件应该嵌套在 中才能使用 connect() 方法。
        • 属性
          • store (Redux Store): 应用程序中唯一的 Redux store 对象
          • children (ReactElement) 组件层级的根组件。
      • connect(mapStateToProps,mapDispatchToProps)
        • 作用
          • 连接 React 组件与 Redux store。连接操作不会改变原来的组件类。反而返回一个新的已与 Redux store 连接的组件类。
        • 属性
          • mapStateToProps(function) : 它接受state作为参数,返回一个对象,这个对象有state里面的状态,代表UI组件的同名参数,它建立一个从store tree 到UI组件props对象的映射关系,执行后返回一个对象,里面每个元素就是一个映射
          • mapDispatchToProps():用来建立 UI 组件的参数到 store.dispatch 方法的映射。它定义了哪些用户的操作应该当作 Action,传给 Store。
    • react-redux 使用

      • index.js //入口文件
          //还是上面的例子
          import React from 'react'
          import { render } from 'react-dom'
          // Provider是react-redux提供的一个组件
          import { Provider } from 'react-redux'
          import App from './App'
          import store from './store'
          render(
            // 一般就直接把这个组件放在应用程序的最顶层,这个组件必须有一个store属性,这个store属性的值就是咱们创建的那个store
            // 只要在最外层包裹了这个Provider, 那么所有后代组件都可以使用Redux.connect做连接
            <Provider store={store}>
              <App />
            </Provider>,
            document.querySelector('#root')
          )
        
      • List.js //组件
        import React, { Component } from 'react'
        // connect方法执行之后是一个高阶组件
        import { connect } from 'react-redux'
        
        // 导入actionCreators
        import { increment, decrement } from '../../actions/cart'
        
        
        @connect(mapState, { increment, decrement })
        // 直接第二个参数传递一个对象,这里面的对象就是actionCreators, 只要传入了actionCreators, 在组件内就通过this.props.actionCreator来调用,这样的话,在调用之后,那个actionCreator就会自动帮你把它内部的action dispatch出去
        也可以使用 mapDispatchToProps
        //// const mapDispatchToProps = dispatch => {
          //   return {
          //     add: (id) => dispatch(increment(id)),
          //     reduce: (id) => dispatch(decrement(id)),
          //   }
          // }
          // @connect(mapState, mapDispatchToProps)
        
        class List extends Component {
        
          render(){
            /////
            ////
            .
            .
            .   和前面相同
            .
            <button onClick={this.props.decrement.bind(this, item.id)}>-</button>
            <span>{item.amount}</span>
            <button onClick={this.props.increment.bind(this, item.id)}>+</button>
          }
        }
        
        // mapStateToProps, 这里的state实际上就store.getState()的值
        const mapstate = (state) => {
          // 这里return了什么,在组件里就可以通过this.props来获取
          return {
            list: state.list
          }
        }
        export default List
        
    • useSelector useDispatch

      • 随着react hooks 提供了一些很好的api,react-redux也在7.1之后引入了Hooks风格类型的api ,useSelector主要是从redux的store中提取数据,useDispatch主要是返回redux store中对dispatch函数的引用。我们可以使用useSelector和useDispatch来代替connect的使用
      • 还是上面的例子,我们做一些改动
          //List.js
          import React from 'react'
          import {useSelector,useDispatch} from 'react-redux'
          function List (){
            const List = useSelector(state => state.list)
            const dispatch = useDispatch()
            return (
              //
              .
              .
              . ....
              .
              {
                list.map((item,index) => {
                  return (
                    ....
                     <button onClick={dispatch({type:'decrement'})}>-</button>
                      <span>{item.amount}</span>
                      <button onClick={dispatch(type:'increment')}>+</button>
                  )
                })
              }
              //
            )
          }
        

    好了,在react项目中基本上也就会用到这几个api,还有些不太常用的就不在这里叙述了,小伙伴们也可以自己尝试去了解和实现一下。其实,dva的出现也为我们简化了开发体验,使用dva配合umi基本上就不需要我们手动引入react-router antd redux react-redux这些了,但是对于redux用户来说,这些基础也还是要熟练掌握的。