Jacky's blog Jacky's blog
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Jacky Wang

行到水穷处,坐看云起时
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Node

  • VSCode插件

  • Webpack

  • Redux

    • redux 源码分析一:常用API介绍
      • 0.前言
      • 1.基本概念和API
        • 1.1 store
        • 1.2 Action 以及 Redux 的工作流程
        • 1.3 Reducer文件
        • 1.4 Redux项目:
      • 2.React-redux
      • 3.总结
      • 4.参考
    • redux 源码分析二:从零开始搭建 redux 源码
  • React源码

  • 组件库

  • React高阶系列

  • UMI插件

  • 前端工程化

  • 单元测试vitest

  • 重点技术
  • Redux
wangjiasheng
2022-02-25
目录

redux 源码分析一:常用API介绍

# 0.前言

本篇博客主要的目的是快速介绍 redux 中的基本概念和API。

# 1.基本概念和API

# 1.1 store

Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store。

Redux 提供createStore这个函数,用来生成 Store对象。

import { createStore } from 'redux';
const store = createStore(fn);
1
2

store 提供了三个方法:

  1. store.getState():获取当前时刻的 state。

  2. store.dispatch():View 发出 Action 的唯一方法。

    store.dispatch({
      type: 'ADD_TODO',
      payload: 'Learn Redux'
    });
    
    1
    2
    3
    4
  3. store.subscribe():设置监听函数,一旦 state 发生变化,就自动执行这个函数。

    常规的搭配:

    let unsubscribe = store.subscribe(() =>
      console.log(store.getState())
    );
    unsubscribe();
    
    1
    2
    3
    4

# 1.2 Action 以及 Redux 的工作流程

在 redux 中对状态的管理如上:

  1. state存放在store 中,View对应着React Components。

  2. View可以通过store.getState()获取到state。但是不允许直接修改store中的state,按照redux规范需要通过ActionCreators对 store 中的值进行操作。

  3. 官方推荐修改 store中的 state最佳实践,如下:

    1. ActionTypes:Magic string

      const ADD_TODO = '添加 TODO';
      
      1
    2. ActionCreators

      export const addTodo = payload => {
        return {
          type: ADD_TODO,
          payload
        }
      }
      
      1
      2
      3
      4
      5
      6
    3. 生成一个Action

      const action = addTodo('Learn Redux');
      
      1
    4. 调用 store.dispatch(),此方法是 View 发出 Action 的唯一方法。

      store.dispatch(action);
      
      1

    上述4个步骤看似复杂,实则等价下面这种写法。

    store.dispatch({type: ADD_TODO,'Learn Redux'});
    
    1

# 1.3 Reducer文件

store 收到 Action 以后,还有一步,即需要告诉计算机ADD_TODO和操作的是state中的value变量,这样 对View的状态更新形成闭环。并且Redux规定接受的必须是一个newState,而计算 newState 的过程就叫做 Reducer。

ps. 从形式来说,看上去是 store 内部对接受到action的一个响应,即对不同的 actionType进行相应的 state的修改,实则reducer的本质是createNewState函数,是dispatch的底层实现,这一点在分析源码时可以发现这一点。

Reducer.js 文件一般如下:

// 1. 包含存入 store 中的 initialState 
const defaultState = 0;

// 2. 接受 state,action => newState (switch case语法)
export const reducer = (state = defaultState, action) => {
  switch (action.type) {
    case 'ADD':
      return state + action.payload;
    default: 
      return state;
  }
};

/*
通过 reducer 函数可以生成一个新的state,以下函数非文件的一部分
const newstate = reducer(1, {ActionType:'ADD',payload:2});
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 1.4 Redux项目:

以下,是一个非常简单的计数器redux项目:完整代码见github (opens new window)

// 1.reducer.js 
const reducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT': return state + 1; // 2.省略 ActionTypes文件
    case 'DECREMENT': return state - 1;
    default: return state;
  }
};

// 3.Components文件夹/Container文件夹
const Counter = ({ value, onIncrement, onDecrement }) => (
  <div>
  <h1>{value}</h1>
  <button onClick={onIncrement}>+</button>
  <button onClick={onDecrement}>-</button>
  </div>
);

// 4.App.js
const store = createStore(reducer);
const render = () => {
  ReactDOM.render(
    <Counter
      value={store.getState()}
      // 5.这里直接传递 {type: 'INCREMENT'} 而省略 ActionCreactor 文件
      onIncrement={() => store.dispatch({type: 'INCREMENT'})} 
      onDecrement={() => store.dispatch({type: 'DECREMENT'})}
    />,
    document.getElementById('root')
  );
};

render();
store.subscribe(render);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

官方推荐的最佳实践项目的拓扑结构 (opens new window),如下所示:

├── actions            # ActionCreators文件
│   └── index.js
├── api                # 公共api
│   ├── products.json
│   └── shop.js
├── components         # 组件文件(小组件)
│   ├── Cart.js
│   ├── Cart.spec.js
│   ├── Product.js
│   ├── Product.spec.js
│   ├── ProductItem.js
│   ├── ProductItem.spec.js
│   ├── ProductsList.js
│   └── ProductsList.spec.js
├── constants
│   └── ActionTypes.js # ActionTypes
├── containers         # 容器文件(大组件)
│   ├── App.js
│   ├── CartContainer.js
│   └── ProductsContainer.js
├── index.js 					 # 存放状态,即store
├── reducers           # reducer文件
│   ├── cart.js
│   ├── cart.spec.js
│   ├── index.js
│   ├── index.spec.js
│   ├── products.js
│   └── products.spec.js
└── setupTests.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 2.React-redux

为了方便使用,Redux的作者封装了一个 React 专用的库 React-Redux (opens new window)。

核心只有一个函数 connect,用于连接全局状态GlobalContext。

具体使用方法如下:

  1. 使用 Provider 创建全局环境。

    import { Provider } from 'react-redux'
    
    render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('root')
    )
    
    1
    2
    3
    4
    5
    6
    7
    8
  2. 使用connect创建一个HOC组件,去连接这个Provider提供的全局环境。

    类似的函数:withRouter 高阶组件

    import { connect } from 'react-redux'
    const HOC_Component = connect(
      mapStateToProps,
      mapDispatchToProps
    )(Component)
    
    1
    2
    3
    4
    5

其中,mapStateToProps和mapDispatchToProps是两个selector,前者用于筛选props,后者用于筛选dispatch。

举例说明:

// 全局仓库有:
state = {
  count : 0
}

// 现有一个Action,用于修改 count
const increaseAction = {type: "increase"}

// 如果不传 `mapStateToProps` 和 `mapDispatchToProps`
// 可以直接从 props 中获取 state 和 dispatch
const HOC_Component = connect(null,null)({state,dispatch}=>{
  console.log(state.count) // 获取 count
  dispatch(increaseAction) // 修改 count
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14

筛选后,对count的获取和修改则更为简单:

const mapStateToProps = (state) => value: state.count

const mapDispatchToProps = dispatc => {
  return {
    onIncreaseClick: () => dispatch(increaseAction)
  }
}

// 使用 selector 后,可直接获取到深层的状态(如state.b.c.d->d)以及修改`d`属性的方法。
const HOC_Component = connect(mapStateToProps,mapDispatchToProps)({count,onIncreaseClick}=>{
  console.log(count) // 获取 count
  onIncreaseClick()  // 修改 count 
})
1
2
3
4
5
6
7
8
9
10
11
12
13

计数器的 react-redux 版本项目:这里 (opens new window)

# 3.总结

本篇博客一共学习到了两个仓库redux和react-redux。

在redux中我们需要了解:

  1. store中存放着,视图层所需的state
    • 读:store.getState()
    • 写:store.dispatch(action)
    • 视图更新:store.subscribe()
  2. 了解 redux 中的项目结构,官方推荐的最佳实践,需要额外创建ActionTypes、ActionCreators等文件,使用时需要手动构建大量的模板代码,这也是redux诟病的地方。

在react-redux中核心的知识点就是:connect的使用

  1. connect是一个高阶的组件,可以快速于全局state绑定。
  2. 两个selector的函数的使用,目的除了起到筛选props和dispatch,简化使用的目的外,实际上还起到精确渲染的作用,通过后续的源码讲解可以对这一点更为清晰的认识。

# 4.参考

本篇博客主要参考了阮一峰的 redux 教程:

  1. Redux 入门教程(一):基本用法 (opens new window)
  2. Redux 入门教程(三):React-Redux 的用法 (opens new window)
编辑 (opens new window)
#redux
上次更新: 2022/10/07, 11:10:00
如何利用requireContext实现批量导入
redux 源码分析二:从零开始搭建 redux 源码

← 如何利用requireContext实现批量导入 redux 源码分析二:从零开始搭建 redux 源码→

最近更新
01
如何理解浏览器的 user agent 用户代理的含义?
11-05
02
浏览器事件循环机制
10-31
03
浏览器页面渲染机制【2023】
10-15
更多文章>
Theme by Vdoing | Copyright © 2020-2023
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式