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介绍
    • redux 源码分析二:从零开始搭建 redux 源码
      • 0.前言
      • 1.从零开始搭建 redux 源码
        • 1.1.初始化项目
        • 1.2.使用 UserModifier 修改 user 信息,并且 User 组件也跟着变化
        • 1.3. createNewState 函数:即 reducer 函数
        • 1.4.将 state 与组件 分离
        • 1.5.使用 createWrapper 函数生成 HOC 组件
        • 1.6.store:解决重复渲染问题
        • 1.7.全局状态订阅
        • 1.8.封装 redux 组件
        • 1.9. mapStateToProps 封装
        • 1.10.精准渲染
        • 1.11. mapDispatchToProps 实现
        • 1.12. connect 接受 selector 的意义
        • 1.13. 封装 createStore
        • 1.14. 封装 Provider 函数
      • 2.大致总结
  • React源码

  • 组件库

  • React高阶系列

  • UMI插件

  • 前端工程化

  • 单元测试vitest

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

redux 源码分析二:从零开始搭建 redux 源码

# 0.前言

本篇博客为 redux项目仓库 (opens new window) 笔记。

# 1.从零开始搭建 redux 源码

# 1.1.初始化项目

// 创建全局的状态
const appContext = React.createContext(null)

// 使用Provider函数包括组件结构
const App = () => {
  const [appState, setAppState] = useState({
    user: { name: "王家盛", age: 18 }
  })
  const contextValue = { appState, setAppState }
  return (
    <appContext.Provider value={contextValue}>
      <Child1></Child1> // 兄弟组件1 - User信息显示
      <Child2></Child2> // 兄弟组件2 - UserModifier
      <Child3></Child3> // 兄弟组件3
    </appContext.Provider>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

需要达到的目标:

  1. 使用 <UserModifier/> 修改 <User/> 组件
  2. 避免重复渲染,修改 user 相关的属性时,只有 <Child1> 和 <Child2> 发生了渲染, <Child3>不发生渲染。

# 1.2.使用 UserModifier 修改 user 信息,并且 User 组件也跟着变化

使用 Hooks 提供默认的状态修改函数 setAppState 对 AppState 的状态进行更新。

const appContext = React.createContext(null)
const { appState, setAppState } = useContext(appContext)
const onChange = (e) => {
    appState.user.name = e.target.value
    setAppState({...appState})
}
1
2
3
4
5
6

在上一次的技术分享中已经阐述了,React 虽然并没有强制要求开发者在开发过程中一定要保持数据的不可变性,但是涉及到 shouldComponentUpdate 这种生命周期函数时,或者 React.memo 等时由于监听不到props的变化,而无法区分 new 值和 old 值。

于是 redux 对这数据的可变性方面进行约束,要求每次必须使用 newState 进行更新(immutable 特性)。

# 1.3. createNewState 函数:即 reducer 函数

ps.函数的命名方面的说明: 在案例中,偏好使用 createNewState 函数去替代 redux 中提供的 reducer 函数名。因为,reducer 本质就是规范化状态更新的作用,createNewState 这种命名可更直接体现这一特性。

const { appState, setAppState } = useContext(appContext)
const onChange = (e) => {
  // 通过newState创建出来的变量,符合 React 对状态Immutable的要求
  const newState = createNewState(appState, "updateUser", { name: e.target.value })
  setAppState(newState)
}
1
2
3
4
5
6

createNewState:用于生成一个 NewState

const createNewState = (state, actionType, payload) => {
  if (actionType === "updateUser") {
    return {
      ...state,
      user: {
        ...state.user,
        ...payload
      }
    }
  } else {
    return state
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

在 redux 中修改一个状态需要接受三个参数:

  1. state:待修改的仓库 state
  2. actionType:执行的 action,目的是对 state 中的哪儿个属性执行什么样的操作。
  3. payload:负载,可传可不传。

# 1.4.将 state 与组件 分离

由上可知,创造一个 NewState 需要接受三个值:state| actionType |payload

实际上,对于用开发者而言,不希望在使用的时候频繁输入: 全局的 state 状态仓库。

于是,通过封装了 dispatch 将输入参数进一步缩小为只需要输入: actionType | payload

于是做了两件事:

  1. 使用 Wrapper 函数将 appState 从 <UserModifier> 中抽离出来。
  2. 使用 dispatch 函数,减少用户重复输入参数 state。
const Wrapper = () => {
  // 通过 HOC :只做一件事,就是将这个组件与全局状态库链接起来。
  const { appState, setAppState } = useContext(appContext)
  const dispatch = (actionType, payload) => {
    const newState = createNewState(appState, actionType, payload)
    setAppState(newState)
  }
  return <UserModifier dispatch={dispatch} state={appState} />
}
1
2
3
4
5
6
7
8
9

Wrapper 本质上就是一个 HOC 组件,作用是将 <UserModifier> 与全局的 state 链接起来。

而 <UserModifier> 则直接通过 props 接受并使用从 HOC 传递过来的 state 和 dispatch。

# 1.5.使用 createWrapper 函数生成 HOC 组件

上面代码存在一个问题,如果每一个需要与全局 state 链接的组件需要重复书写上面这段代码,太过繁琐。

于是可以通过构造 createWrapper 函数,封装 Wrapper 代码的生成过程,代码如下:

这部分也可以通过装饰器的方式实现

// 使用 createWrapper 批量化生成 HOC 组件 ,即connect
const createWrapper = (Component) => {
  return (props) => {
    const { appState, setAppState } = useContext(appContext)
    const dispatch = (actionType, payload) => {
      const newState = createNewState(appState, actionType, payload)
      setAppState(newState)
    }
    return <Component dispatch={dispatch} state={appState} />
    // return <Component {...props} dispatch={dispatch} state={appState} />
  }
}

// 使用时,直接传入需要链接的组件 <UserModifier />
const Wrapper2 = createWrapper(UserModifier)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

进一步,封装的 Wrapper 还需将自身的 props 传递给 <UserModifier>

return <Component {...props} dispatch={dispatch} state={appState} />
1

# 1.6.store:解决重复渲染问题

上述过程中,我们是使用全局状态提供的方法对状态进行修改的:

const { appState, setAppState } = useContext(appContext)
1

但是在 React-Hooks 中渲染的原则是:一旦调用了setXXXX方法,对应的 Appstate 就会进行渲染,导致所有依赖此 state (被包裹在useContext.Provider下)的组件都会被重复渲染。

解决方案:创建一个store对象,自己来维护全局state 以及修改这个 state的方法,以此来替代 的 react-Hooks 提供的setAppState函数。

const store = {
  appState: {
    user: { name: "王家盛", age: 18 }
  },
  setAppState(newState) {
    console.log('newState', newState);
    store.appState = newState
  }
}
1
2
3
4
5
6
7
8
9

仅是上面这段代码是无法对视图进行更新的,需要手动触发更新:

ps.这一点和React相同,需要通过 setState 方法对 view 进行更新,无法通过 this.props.state.xxx=xxx 。

// 显式地调用 setXXXX 方法,达到主动触发 视图刷新 的作用
const [, update] = useState({})
const dispatch = (actionType, payload) => {
  setAppState(createNewState(appState, actionType, payload))
  // 在 dispatch 后刷新
  update({})
}
1
2
3
4
5
6
7

# 1.7.全局状态订阅

通过 [,update] = useState() 刷新视图的方法存在一个问题:

createWrapper(connect) 会单独生成一个 dispatch 函数, 于是每一个connect的组件,只会刷新自己的状态,而无法把state 的变化 映射到 所有依赖这个state 的组件中。

解决方法: 使用 eventhub,订阅 state 的变化。一旦某个state 变化,就将全局订阅 state的组件依次进行渲染。

const store = {
  appState: {
    user: { name: "王家盛", age: 18 }
  },
  setAppState(newState) {
    console.log('newState', newState);
    store.appState = newState
    store.listeners.map(fn => fn(store.appState))
  },
  listeners: [], // 简易版的 EventHub
  subscribe(fn) {
    store.listeners.push(fn)
    return () => {
      const index = store.listeners.indexOf(fn)
      store.listeners.splice(index, 1)
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

改造后,可在 HOC 将自己组件的 update() 加入订阅:(只要 store 变化,即刷新页面)

const createWrapper = (Component) => {
  return (props) => {
    const { appState, setAppState } = useContext(appContext)
    // 显式地调用 setXXXX 方法,达到精准的控制 视图刷新 的功能
    const [, update] = useState({})
    useEffect(() => {
      store.subscribe(() => {
        update({})
      })
    }, [])

    const dispatch = (actionType, payload) => {
      setAppState(createNewState(appState, actionType, payload))
    }
    return <Component {...props} dispatch={dispatch} state={appState} />
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
image-20220302202626362

# 1.8.封装 redux 组件

接下来稍微梳理下需要被抽离的函数:

  1. createWrapper:即,connect 函数部分功能
  2. createNewState:即,reducer 函数(规范化state的创建流程)
  3. 提供store对象,其中封装了以下内容:
    • appState:全局的状态
    • setAppState:自己实现修改state的方法,目的是代替React-Hooks提供的setXXX 函数。
    • EventHub:简易版的事件发布订阅函数。

# 1.9. mapStateToProps 封装

使用方法说明:

改造 connect 函数,使其能够接收两个函数(selector,dispatchSelector),改造后可以对 state和 dispatch 方法进行过滤或者拦截。即,将 state 和 dispatch 进行一层封装,不再直接提供状态和方法,而是封装后弹出。

在这小节主要实现的是mapStateToProps,也就是connect 函数接收的第一个参数,本质上实现的是 state的selector。

使用时可以直接获取深层数据,如:state.xxx.yyy.zzz

store = {
  state:{
   	xxx:{
      yyy:{
        zzz:"123"
      }
    }
  }
}
1
2
3
4
5
6
7
8
9

mapStateToProps 函数中可以定义映射规则:

const mapStateToProps = (state) =>{
  return { zzz: state.xxx.yyy.zzzz }
}
1
2
3

映射后,将属性作为 props 传递到被包裹的 Component 中:

connect(mapStateToProps,null)(({zzz})=>{
  .....
})
1
2
3

实现原理:

修改前:

return <Component {...props} state={appState} dispatch={dispatch}>
1

修改后: appState 会先经过一层 selector 后传递到 Component 的 props

const data = selector ? selector(appState) : { appState: appState }
return <Component {...props} {...data} {...dispatch} />
1
2

# 1.10.精准渲染

在这一层对属性进行过滤比较,并解决非依赖属性的重复渲染问题。

本质上:就是做了一层 props.state diff 的操作。

// 使用 connect 批量化生成 HOC 组件 ,即connect
export const connect = (selector) => (Component) => {
    return (props) => {
        const { appState, setAppState } = useContext(appContext)
        const [, update] = useState({})
   +    const data = selector ? selector(appState) : { appState: appState }
        useEffect(() => {
            store.subscribe(() => {
   +            const newData = selector ? selector(store.appState) : { appState: store.appState }
   +            if (changed(data, newData)) {
                // 这里可以对state进行精准控制 diff
                    update({})
                }
            })
        // 可以尝试下将 [selector] 设置成 [selector,appState] ,观察“视图真实update”的执行数量:2——>4——>6
   +    }, [selector])

        const dispatch = (actionType, payload) => {
            setAppState(reducer(appState, actionType, payload))
        }
        return <Component {...props} dispatch={dispatch} {...data} />
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 1.11. mapDispatchToProps 实现

+ export const connect = (selector, dispatchSelector) => (Component) => {
    return (props) => {
      const dispatch = (actionType, payload) => {
        setAppState(reducer(appState, actionType, payload))
      }
      const { appState, setAppState } = useContext(appContext)
      const [, update] = useState({})
      const data = selector ? selector(appState) : { appState: appState }
+     const dispatchers = dispatchSelector ? dispatchSelector(dispatch) : { dispatch: dispatch }
      useEffect(() => {
        const unsubscribe = store.subscribe(() => {
          const newData = selector ? selector(store.appState) : { appState: store.appState }
          if (changed(data, newData)) {
            // 这里可以对state进行精准控制
            update({})
          }
        })
        return unsubscribe
        // 此时依赖改为 [selector,appState] 也不会重复订阅。每次值变化的时,先执行return的unsubscribe函数。
      }, [selector])

+     return <Component {...props} {...dispatchers} {...data} />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 1.12. connect 接受 selector 的意义

在上述步骤中,connect 主要实现了以下功能:

  1. 是一个HOC,目的是与全局的store 进行,本质上就是一个createWrapper。
  2. 接受两个参数:mapStateToProps,mapDispatchToProps,可以实现深层数据的快速获取,以及选中属性的精确渲染。

除了以上的功能外,可以通过单独构建connections文件,实现对全局状态的拆分。

// 创建一个专门修改 User 对象的环境(即,只暴露全局与User有关的属性和修改属性的方法)
const mapStatetoProps = state => {
  return { group: state.group }
}

const mapDispatchToProps = dispatch => {
  return {
    updateUser: (attrs) => dispatch("updateUser", attrs)
  }
}

const connectToUser = connect(mapStatetoProps, mapDispatchToProps)
1
2
3
4
5
6
7
8
9
10
11
12

在开发阶段,我们可以直接使用 connectToUser 代替 connect 函数进行状态获取。

// 导入 connecters
import { connectToUser } from "./connecters/connectToUser"
1
2

综上,connect 的意义见下图所示:

  1. 通过 mapStateToProps 函数store可被进一步划分,返回的结果是只与 User 状态项链接的一个 createWrapper 函数,可用于生成 HOC ( Wrapper ) 高阶组件。
  2. 不同颜色的Wrapper在状态发生变化的时候,只会渲染与自己相关联的Wrapper。
  3. 在状态封装方面,下图左侧部分的一般可由经验丰富的程序员预先封装,而业务开发的时候可直接调用这些封装好的函数进行HOC 的生成。

# 1.13. 封装 createStore

至此 redux 的源码的核心原理基本上结束了,下面开始都是封装技巧。

之前为了演示方便,直接将 reducer 和 state 是写死在redux.js文件中,而这两个部分应由开发人员编写后,显示传递到store中。

构造 createStore 函数:

// 将 reducer 和 state 中从 `store` 中抽出,由外部传入。
const store = {
    appState: undefined,
    reducer: undefined,
    .........
}
  
export const createStore = (reducer, initState) => {
    store.reducer = reducer
    store.appState = initState
    return store
}
1
2
3
4
5
6
7
8
9
10
11
12

# 1.14. 封装 Provider 函数

就是将appContext.Provider

<appContext.Provider value={store}>
  <Child1></Child1>
  <Child2></Child2>
  <Child3></Child3>
</appContext.Provider>
1
2
3
4
5

改造为:

<Provider store={store}>
  <Child1></Child1>
  <Child2></Child2>
  <Child3></Child3>
</Provider>
1
2
3
4
5

代码:

// 封装 Provider 
export const Provider = ({ store, children }) => {
    return (
        <appContext.Provider value={store}>
            {children}
        </appContext.Provider>
    );
}
1
2
3
4
5
6
7
8

# 2.大致总结

  • reducer 函数:本质上就是createNewState函数,需要接受三个参数才能完成状态state的修改。

  • dispatch 函数:是对 createNewState 函数进行封装的,抽离出全局的state,简化成输入两个参数(即,action:{actionType,payload})。

  • createWrapper 函数:connect 函数的前身,主要用于批量化生成 HOC 组件。(核心中的核心)

  • store 对象的封装:

    • 全局 state 存储。
    • subscribe 函数,即 eventHub 的 on 函数。
    • listener 监听器,用于存放订阅函数 update({})。
  • connect 函数封装:

    • 接受两个参数:mapStateToProps、mapDispatchToProps,本质就是 selector 函数。
    • 用于和全局状态进行链接,connect(null,null)(Component) 等价于createWrapper 函数
    • 如果 mapStateToProps 有值,则 createWrapper 可以和特定state进行链接,并且可以做到对筛选 state 精确渲染。
  • Provider 与 createStore 函数只是在原有函数的基础上进行封装。

  • ActionTypes、ActionCreators等都属于 action 模板写法。

编辑 (opens new window)
#redux
上次更新: 2022/10/07, 11:10:00
redux 源码分析一:常用API介绍
React源码阅读[TODO]

← redux 源码分析一:常用API介绍 React源码阅读[TODO]→

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