5.react Redux

256 阅读5分钟

参考:juejin.cn/post/684490…

Redux 是 JavaScript 状态容器,提供可预测化的状态管理。类似vuex的全局存储状态。

Redux 由 Flux 演变而来,但受 Elm 的启发,避开了 Flux 的复杂性。 不管你有没有使用过它们,只需几分钟就能上手 Redux。

1.Redux 的三大原则:

  • 单一数据源:整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中
  • State 是只读的:唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象
  • 使用纯函数来执行修改:为了描述 action 如何改变 state tree ,你需要编写 reducers

不使用redux,react兄弟组件传值图

image.png

2.Redux工作流向图

image.png

3.Action

它是 store 数据的唯一来源。你会通过 store.dispatch() 将 action 传到 store(Redux没经过处理的dispatch()方法只能同步更新state树中的数据). 定义一个Action类型

在组件中通过dispatch()调用创建函数


4.Reducer(纯函数)

Reducers 指定了应用状态的变化如何响应 actions 并发送到 store 的,记住 actions 只是描述了有事情发生,state的更新只发生在Reducer且Reducer不会修改原来state的数据,而是生成新数据响应到store

注意永远不要在Reducer里面干这些事情:

  • 不要修改state
  • 在 default 情况下返回旧的 state。遇到未知的 action 时,一定要返回旧的 state。
  • 执行有副作用的操作,如 API 请求和路由跳转;
  • 调用非纯函数,如 Date.now() 或 Math.random()

为什么要这样呢?

纯函数概念 :

  • 同的输入永远返回相同的输出
  • 不修改函数的输入值
  • 不依赖外部环境状态
  • 无任何副作用

Redux通过比较新旧两个对象是否一致,这种较法,比较的是两个对象的存储位置,也就是浅比较法,所以,当我们 reducer 直接返回旧的 state 对象时,Redux 认为没有任何改变,从而导致页面没有更新。因为比较两个 javascript 对象中所有的属性是否完全相同,唯一的办法就是深比较,然而,深比较在真实的应用中代码是非常大的,非常耗性能的,需要比较的次数特别多,所以一个有效的解决方案就是做一个规定,当无论发生任何变化时,开发者都要返回一个新的对象,没有变化时,开发者返回旧的对象,这也就是 redux 为什么要把 reducer 设计成纯函数的原因

5.Store

Store 就是把action和reducer联系到一起的对象,Redux 应用只有一个单一的 store

  • 维持应用的 state;
  • 提供 getState() 方法获取 state;
  • 提供 dispatch(action) 方法更新 state;
  • 通过 subscribe(listener) 注册监听器;
  • 通过 subscribe(listener) 返回的函数注销监听器

6.搭配React使用

Redux 和 React 之间没有关系。Redux 支持 React、Angular、Ember、jQuery 甚至纯 JavaScript。

安装React绑定库

npm install --save react-redux

7. Action异步处理数据

为确保Reducer不产生任何副作用,redux 把异步处理数据交给Action, 当初规定Action必须是一个纯对象,是出于实际需要。因为reducer必须是一个纯函数,这决定了dispatch的参数Action必须是一个带type字段的纯对象。现如今我们要加入异步请求,而中间件又可以中途拦截做一些处理,所以我们需要对函数式的Action做一些处理,引入Redux Thunk 中间件,Redux中间件的作用: 改造store.dispatch,解决异步操作

8.安装状态数据可视化工具redux-devtools

redux-devtools gitup地址 github.com/reduxjs/red…

rematch:

虽然现在写的项目封装过,导致差异很大。

因为@rematch/core 集成导致 参考:www.cnblogs.com/mengff/p/95…

rematch是对redux的二次封装,简化了redux是使用,极大的提高了开发体验。

1. rematch的优点

1.省略了action types不必再多次写字符串,使用model/method代替

2.省略了action creators直接调用方法,不必再生产action type,使用dispatch.model.method代替

3.省略了switch语句调用model.method方法,不必判断action type

4.集中书写状态,同步和异步方法在一个model中使用state,reducers和effects来写状态,同步和异步方法

2. rematch的model

model中直接写state,reducers,effects,十分集中方便


export const count = {

    state: 0, // initial state

    reducers: {

        // handle state changes with pure functions

        increment(state, payload) {

            return state + payload

        }

    },

    effects: (dispatch) => ({

        // handle state changes with impure functions.

        // use async/await for async actions

        async incrementAsync(payload, rootState) {

        await new Promise(resolve => setTimeout(resolve, 1000))

        dispatch.count.increment(payload)

        }

    })

}

3. rematch的dispatch

dispatch可以直接调用同步和异步方法,不必再发送action


import { init } from '@rematch/core'

import * as models from './models'

const store = init({

models,

})

export const { dispatch } = store // state = { count: 0 }

// reducers

dispatch({ type: 'count/increment', payload: 1 }) // state = { count: 1 }

dispatch.count.increment(1) // state = { count: 2 }

// effects

dispatch({ type: 'count/incrementAsync', payload: 1 }) // state = { count: 3 } after delay

dispatch.count.incrementAsync(1) // state = { count: 4 } after delay

4. rematch的状态派发

依然使用redux的connect,mapStateToProps,mapStateToDispatch来派发状态和方法到子组件

import React from 'react'

import ReactDOM from 'react-dom'

import { Provider, connect } from 'react-redux'

import store from './index'

const Count = props => (

    <div>

        The count is {props.count}

        <button onClick={props.increment}>increment</button>

        <button onClick={props.incrementAsync}>incrementAsync</button>

    </div>

)

const mapState = state => ({

    count: state.count

})

const mapDispatch = ({ count: { increment, incrementAsync }}) => ({

    increment: () => increment(1),

    incrementAsync: () => incrementAsync(1)

})

const CountContainer = connect(mapState, mapDispatch)(Count)

ReactDOM.render(

    <Provider store={store}>

    <CountContainer />

    </Provider>,

    document.getElementById('root')

)

疑问:state放在Redux Store上还是放在React上呢?

即需要去控制显示时,是通过内部state还是Redux的state去控制


    1. 这个state是否其他组件也会用得上?
    2. 这个state是否当组件被unmount然后再mount的时候还要存在

只有这两个问题的回答都是No的时候,才可以放在React中,否则放在Redux Store中