Redux的三大核心

1,852 阅读9分钟

Redux基本介绍

redux

文档:redux 中文 redux 英文

Redux 是 JavaScript 应用的状态容器,提供可预测的状态管理。

当遇到如下问题时,建议开始使用 Redux:

  • 你有很多数据随时间而变化
  • 你希望状态有一个唯一确定的来源
  • 你发现将所有状态放在顶层组件中管理已不可维护

redux和react的关系

Redux并不只为react应用提供状态管理, 它还支持其它的框架。

为什么Rect要用 Redux

React 是 DOM 的一个抽象层(UI 库),并不是 Web 应用的完整解决方案。因此react在涉及到数据的处理以及组件之间的通信时会比较复杂。

对于大型的复杂应用来说,这两方面恰恰是最关键的。因此,只用 React,写大型应用比较吃力。

  • 2014 年 Facebook 提出了 Flux 架构的概念,引发了很多的实现。
  • 2015 年,Redux 出现,将 Flux 与函数式编程(reducer)结合一起,很短时间内就成为了最热门的前端架构。
  • Flux 是最早的状态管理 工具,它提供了状态管理的思想,也提供对应的实现
  • 除了 Flux、Redux 之外,还有:Mobx 等状态管理工具

image.png

  • 主要的区别:组件之间的通讯问题

  • 不使用 Redux (图左边) :

    • 只能使用父子组件通讯、状态提升等 React 自带机制
    • 处理远房亲戚(非父子)关系的组件通讯时乏力
    • 组件之间的数据流混乱,出现 Bug 时难定位
  • 使用 Redux (图右边):

    • 集中式存储和管理应用的状态
    • 处理组件通讯问题时,无视组件之间的层级关系
    • 简化大型复杂应用中组件之间的通讯问题
    • 数据流清晰,易于定位 Bug

Redux 三个核心概念

理解三个核心概念

核心概念:storeactionreducer

action

  • 动作。

  • 一个js对象,包含两个属性:

    • type: 标识属性,值是字符串。多个type用action分开
    • payload:数据属性,可选。表示本次动作携带的数据
  • actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state。

  • 特点:

    • 只描述做什么
    • JS 对象,必须带有 type 属性,用于区分动作的类型
    • 根据功能的不同,可以携带额外的数据,配合该数据来完成相应功能

reducer

  • 一个纯函数

  • 作用

      1. 初始化状态
      2. 修改状态
  • 修改状态

    根据传入的旧状态和action,返回新状态

    公式:(previousState, action) => newState

store

  • store:仓库,Redux 的核心,整合 action 和 reducer

  • 特点:

    • 一个应用只有一个 store
    • 维护应用的状态,获取状态:store.getState()
    • 创建 store 时接收 reducer 作为参数const store = createStore(reducer)
    • 发起状态更新时,需要分发 action:store.dispatch(action)
  • 其他 API, — 订阅(监听)状态变化:const unSubscribe = store.subscribe(() => {}) — 取消订阅状态变化: unSubscribe()

安装

npm i redux

Redux功能演示核心代码

定义reuder

initState = 0
function reducer(state = initState, action) {
  return state
}

定义action

const action1 = { type:'addN', payload: 12 }
// store.dispatch(action1)

const action2 = { type:'add', payload: 1 }

定义store

import { createStore } from 'redux'
// 创建 store
const store = createStore(reducer)

store 相关API

  1. store获取状态 store.getState()
  2. store修改数据,通过派发action。store.dispatch({type:xxx, payload: xx}})
  3. store添加订阅者。
//  格式:
//  取消订阅 = store.subscribe(订阅者)

//  订阅者:就是一个函数,当state值变化时,store会执行它
//  store.subscript()的返回值也是一个函数,执行它时,它的
const unSubscribe = store.subscribe(() => {
  // 状态改变时,执行相应操作
})

// 取消监听状态变化
// unSubscribe()

4.取消订阅者。unSubscribe()

核心代码

import { createStore } from 'redux'

initState = 0
function reducer(state = initState, action) {
  return state
}
// 创建 store
const store = createStore(reducer)

// store获取状态
console.log(store.getState())

// 更新状态
//     1. dispatch 派遣,派出。表示:分发一个 action,也就是发起状态更新
//     2. store.dispatch会 调用reducer函数,并将action传递给reducer
const action1 = { type:'addN', payload: 12 }
store.dispatch(action1)

const action2 = { type:'add', payload: 1 }
store.dispatch(action2)

// store 添加订阅者
//  store.subscribe
//  订阅者:就是一个函数,当state值变化时,store会执行它
const unSubscribe = store.subscribe(() => {
  // 状态改变时,执行相应操作
  console.log('数据变化了....')
})

// 取消监听状态变化
unSubscribe()

Redux 代码执行过程

获取默认值

只要创建 store,那么,Redux 就会调用一次 reducer, 且type是一个随机值。如下是一个示例:

image.png

  • type是随机值确保了它不会被用户业务逻辑处理,而只能去匹配默认值。

这一次调用 reducer 的目的:获取状态的默认值。这个初始值将成为下一次调用 store.getState() 方法来获取 Redux 状态值的preState

更新状态

  1. 当你需要更新状态时,就先分发动作 store.dispatch(action)
  2. Redux 内部,store 就会调用 reducer,传入:上一次的状态(当前示例中就是:0)和 action({ type: 'add' }),计算出新的状态,并返回这个新值。
  3. reducer 执行完毕后,将最新的状态交给 store,store 用最新的状态替换旧状态,状态更新完毕
import { createStore } from 'redux'
const store = createStore(reducer)

function reducer(state = 0, action) {
  console.log('reducer:', state, action)
  switch (action.type) {
    case 'add':
      return state + 1
    default:
      return state
  }
}

console.log('状态值为:', store.getState()) // 10

// 发起更新状态:
// 参数: action 对象
store.dispatch({ type: 'increment' })
// 相当于: reducer(10, { type: 'increment' })

console.log('更新后:', store.getState()) // 11

React-redux基本介绍

react中直接使用redux有很多不方便的地方

  1. 每个组件都需要单独导入store
  2. 在根组件上的写法不友好

react-redux 库

是 Redux 官方提供的 React 绑定库

  1. React 和 Redux 是两个独立的库,两者之间职责独立。
  2. Redux可以和其他的js库,框架一起使用,而并不专门用于react。
  3. 为了实现在 React 中使用 Redux 进行状态管理 ,就需要一种机制,将这两个独立的库关联在一起。这时候就用到 React-Redux 这个绑定库了
  4. 作用: 为 React 接入 Redux,实现在 React 中使用 Redux 进行状态管理。

react-redux-基本使用

步骤

  1. 安装 npm i react-redux

  2. 使用

    1. 按redux的要求,创建好store, reducer,action等等
    2. 从react-redux中引入 provider, useSelector, useDispatch来 操作 redux

API

Provider

  • 用法:直接包装在根组件上。<Provider store={store}>
  • 好处:相比react + redux,这样就不需要每个组件都引入store了

useSelector

  • 用法:获取公共状态

  • 好处:

    • 相比react + redux,不需要使用store.getState()了
    • state变化了,它会自动更新
  • 格式:const 状态 = useSelector(store的数据 => 你需要的部分)

useDispatch

  • 用法:派发action,修改数据
  • 格式: const dispatch = useDispatch(); dispatch(action)

action creator

问题导入

dispatch({type: 'book/add', payload: '三国演义'})
dispatch({type: 'book/add', payload: '红楼梦'})

一个action的type名字出现多次,多次dispatch会重复,不利于修改。

思路

补充一个action creator。

  • 它是一个函数。
  • 作用:用来创建指定类型的action。
  • 入参:payload
  • 返回值: action
  • 示例:
const addAction = (payload) => ({type: 'book/add', payload})
dispatch(addAction('三国演义'))
dispatch(addAction('红楼梦'))

Action Type的使用

问题

背景:在reducer和action creator中都用到了一个字符串的 action type。

问题:容易写错,不利于统一修改。

解决

思路:集中处理 action type,保持项目中 action type 的一致性

处理方式:

  1. 在 store 目录中创建 actionTypes 目录或者 constants 目录,集中处理
  2. 使用常量来存储 action type。例如
export const SET_NAME = 'user/setName'
export const SUB_MORE = 'money/subMore'

3.将项目中用到 action type 的地方替换为这些常量,从 而保持项目中 action type 的一致性

redux-中间件

方案

方案1:在业务组件中发请求,拿到数据之后,再dispath image.png

方案2:在业务组件中直接dispatch,在dispatch中发请求,然后保存redux。

image.png

中间件

中间件:middleware。用来在不损害原功能的前提下,引入额外的代码来拓展功能。 Redux 中间件

  • 中间:在 dispatch action 和 到达 reducer 之间

图示

没有中间件

  • dispatch(action) => reducer。用来发起状态更新

image.png

使用中间件

  • dispatch(action) => 执行中间件代码 => reducer
  • dispatch() 就是 中间件 封装处理后的 dispatch,但是,最终一定会调用 Redux 库自己提供的 dispatch 方法

image.png

redux-中间件-redux-thunk

作用

redux-thunk 中间件允许redux处理函数形式的 action。在函数形式的 action 中就可以执行异步操作代码,完成异步操作。

之前

const action1 = {type: 'todos/add', payload: '学习redux'}
dispatch(action1)

之后

const action1 = async (dispatch) =>{
  const res = await 异步动作()
  dispatch({type: 'todos/add', payload: '学习redux'})
}

dispatch(action1)

步骤

  1. 安装:npm i redux-thunk

  2. 使用:在store/index.js

    1. 导入 redux-thunk , applyMiddleware
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'

2.调用applyMiddleware将 thunk 添加到中间件列表中

const store = createStore(rootReducer, applyMiddleware(thunk))

3.修改 action creator,返回一个函数,其形参就是redux的dispatch

const addTodo = (name)=> {
  return async (dispatch) =>{
    const res = await 异步动作()
    dispatch({type: 'todos/add', payload: name})
  }
}

dispatch(addTodo('学习redux'))

redux-中间件-redux-logger

步骤

  1. 安装:npm i redux-logger

  2. 使用。store/index.js

    1. 导入 redux-logger
    2. 调用 applyMiddleware 函数时,将 logger 作为参数传入
import { createStore, applyMiddleware } from 'redux'
import logger from 'redux-logger'
import rootReducer from './reducers'
const store = createStore(rootReducer, applyMiddleware(logger))

3.测试效果

任意调用 store.dispatch() 查看 logger 中间件记录的日志信息

redux-devtools-extension的使用

redux-devtools-extension

方便在浏览器中调试redux操作的工具

先要安装 redux的开发者工具,再安装redux调试工具

文档 redux-devtools-extension

步骤

  1. 安装react开发者工具(chrome浏览器插件)
  2. 安装redux的开发者工具(chrome浏览器插件)
  3. 在项目中安装redux调试工具,它是一个npm包。 npm i redux-devtools-extension -D
  4. 配置。在store/index.js中进行配置和导入
import { createStore, applyMiddleware } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
const store = createStore(reducer, composeWithDevTools(applyMiddleware(中间件..)))

export default store

redux-thunk-中间件原理

redux-thunk的源码非常简单

注意下面的typeof action === 'function'

function createThunkMiddleware(extraArgument) {
  // Redux 中间件的写法:const myMiddleware = store => next => action => { /* 此处写 中间件 的代码 */ }
  return ({ dispatch, getState }) => (next) => (action) => {
    // redux-thunk 的核心代码:
    // 判断 action 的类型是不是函数
    // 如果是函数,就调用该函数(action),并且传入了 dispatch 和 getState
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }
    
    // 如果不是函数,就调用下一个中间件(next),将 action 传递过去
    // 如果没有其他中间件,那么,此处的 next 指的就是:Redux 自己的 dispatch 方法
    return next(action);
  };
}

宝,你都看到这了不给我一个star嘛?

PS:   如果内容有错误的地方欢迎指出(觉得看着不理解不舒服想吐槽也完全没问题);如果有帮助,欢迎点赞和收藏,转载请著明出处,如果有问题也欢迎私信交流