Lesson 31 动手实现 Redux(二):抽离 store 和监控数据变化
发布日期:2022-09-10 03:02:15 浏览次数:5 分类:技术文章

本文共 5288 字,大约阅读时间需要 17 分钟。

Lesson 31 动手实现 Redux(二):抽离 store 和监控数据变化

抽离出 store

的我们有了 appStatedispatch

let appState = {
title: {
text: 'React.js 小书', color: 'red', }, content: {
text: 'React.js 小书内容', color: 'blue' }}function dispatch (action) {
switch (action.type) {
case 'UPDATE_TITLE_TEXT': appState.title.text = action.text break case 'UPDATE_TITLE_COLOR': appState.title.color = action.color break default: break }}

现在我们把它们集中到一个地方,给这个地方起个名字叫做 store,然后构建一个函数 createStore,用来专门生产这种 statedispatch 的集合,这样别的 App 也可以用这种模式了:

function createStore (state, stateChanger) {
const getState = () => state const dispatch = (action) => stateChanger(state, action) return {
getState, dispatch }}

createStore 接受两个参数,一个是表示应用程序状态的 state;另外一个是 stateChanger,它来描述应用程序状态会根据 action 发生什么变化,其实就是相当于本节开头的 dispatch 代码里面的内容。

createStore 会返回一个对象,这个对象包含两个方法 getStatedispatchgetState 用于获取 state 数据,其实就是简单地把 state 参数返回。

dispatch 用于修改数据,和以前一样会接受 action,然后它会把 stateaction 一并传给 stateChanger,那么 stateChanger 就可以根据 action 来修改 state 了。

现在有了 createStore,我们可以这么修改原来的代码,保留原来所有的渲染函数不变,修改数据生成的方式:

let appState = {
title: {
text: 'React.js 小书', color: 'red', }, content: {
text: 'React.js 小书内容', color: 'blue' }}function stateChanger (state, action) {
switch (action.type) {
case 'UPDATE_TITLE_TEXT': state.title.text = action.text break case 'UPDATE_TITLE_COLOR': state.title.color = action.color break default: break }}const store = createStore(appState, stateChanger)renderApp(store.getState()) // 首次渲染页面store.dispatch({
type: 'UPDATE_TITLE_TEXT', text: '《React.js 小书》' }) // 修改标题文本store.dispatch({
type: 'UPDATE_TITLE_COLOR', color: 'blue' }) // 修改标题颜色renderApp(store.getState()) // 把新的数据渲染到页面上

针对每个不同的 App,我们可以给 createStore 传入初始的数据 appState,和一个描述数据变化的函数 stateChanger,然后生成一个 store。需要修改数据的时候通过 store.dispatch,需要获取数据的时候通过 store.getState

监控数据变化

上面的代码有一个问题,我们每次通过 dispatch 修改数据的时候,其实只是数据发生了变化,如果我们不手动调用 renderApp,页面上的内容是不会发生变化的。但是我们总不能每次 dispatch 的时候都手动调用一下 renderApp,我们肯定希望数据变化的时候程序能够智能一点地自动重新渲染数据,而不是手动调用。

你说这好办,往 dispatch里面加 renderApp 就好了,但是这样 createStore 就不够通用了。我们希望用一种通用的方式“监听”数据变化,然后重新渲染页面,这里要用到观察者模式。修改 createStore

function createStore (state, stateChanger) {
const listeners = [] const subscribe = (listener) => listeners.push(listener) const getState = () => state const dispatch = (action) => {
stateChanger(state, action) listeners.forEach((listener) => listener()) } return {
getState, dispatch, subscribe }}

我们在 createStore 里面定义了一个数组 listeners,还有一个新的方法 subscribe,可以通过 store.subscribe(listener) 的方式给 subscribe 传入一个监听函数,这个函数会被 push 到数组当中。

我们修改了 dispatch,每次当它被调用的时候,除了会调用 stateChanger 进行数据的修改,还会遍历 listeners 数组里面的函数,然后一个个地去调用。相当于我们可以通过 subscribe 传入数据变化的监听函数,每当 dispatch 的时候,监听函数就会被调用,这样我们就可以在每当数据变化时候进行重新渲染:

const store = createStore(appState, stateChanger)store.subscribe(() => renderApp(store.getState()))renderApp(store.getState()) // 首次渲染页面store.dispatch({
type: 'UPDATE_TITLE_TEXT', text: '《React.js 小书》' }) // 修改标题文本store.dispatch({
type: 'UPDATE_TITLE_COLOR', color: 'blue' }) // 修改标题颜色// ...后面不管如何 store.dispatch,都不需要重新调用 renderApp

对观察者模式不熟悉的朋友可能会在这里晕头转向,建议了解一下这个设计模式的相关资料,然后进行练习: 再进行阅读。

我们只需要 subscribe 一次,后面不管如何 dispatch 进行修改数据,renderApp 函数都会被重新调用,页面就会被重新渲染。这样的订阅模式还有好处就是,以后我们还可以拿同一块数据来渲染别的页面,这时 dispatch 导致的变化也会让每个页面都重新渲染:

const store = createStore(appState, stateChanger)store.subscribe(() => renderApp(store.getState()))store.subscribe(() => renderApp2(store.getState()))store.subscribe(() => renderApp3(store.getState()))...

本节的完整代码:

function createStore (state, stateChanger) {
const listeners = [] const subscribe = (listener) => listeners.push(listener) const getState = () => state const dispatch = (action) => {
stateChanger(state, action) listeners.forEach((listener) => listener()) } return {
getState, dispatch, subscribe }}function renderApp (appState) {
renderTitle(appState.title) renderContent(appState.content)}function renderTitle (title) {
const titleDOM = document.getElementById('title') titleDOM.innerHTML = title.text titleDOM.style.color = title.color}function renderContent (content) {
const contentDOM = document.getElementById('content') contentDOM.innerHTML = content.text contentDOM.style.color = content.color}let appState = {
title: {
text: 'React.js 小书', color: 'red', }, content: {
text: 'React.js 小书内容', color: 'blue' }}function stateChanger (state, action) {
switch (action.type) {
case 'UPDATE_TITLE_TEXT': state.title.text = action.text break case 'UPDATE_TITLE_COLOR': state.title.color = action.color break default: break }}const store = createStore(appState, stateChanger)store.subscribe(() => renderApp(store.getState())) // 监听数据变化renderApp(store.getState()) // 首次渲染页面store.dispatch({
type: 'UPDATE_TITLE_TEXT', text: '《React.js 小书》' }) // 修改标题文本store.dispatch({
type: 'UPDATE_TITLE_COLOR', color: 'blue' }) // 修改标题颜色

总结

现在我们有了一个比较通用的 createStore,它可以产生一种我们新定义的数据类型 store,通过 store.getState 我们获取共享状态,而且我们约定只能通过 store.dispatch 修改共享状态。store 也允许我们通过 store.subscribe 监听数据数据状态被修改了,并且进行后续的例如重新渲染页面的操作。

当前内容版权归 或其关联方所有,如需对内容或内容相关联开源项目进行关注与资助,请点击 .

最初的起点:返回全书目录

上一篇:

下一篇:

转载地址:https://blog.csdn.net/Mr_zzr/article/details/125796656 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Lesson 32 动手实现 Redux(三):纯函数(Pure Function)简介
下一篇:Lesson 30 动手实现 Redux(一):优雅地修改共享状态

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2024年03月21日 11时18分11秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

【Leetcode刷题篇】leetcode538 把二叉搜索树转换为累加树 2021-06-29
【多线程与高并发】线程的优先级是怎么回事? 2021-06-29
【多线程与高并发】Java守护线程是什么?什么是Java的守护线程? 2021-06-29
【Leetcode刷题篇/面试篇】-前缀树(Trie) 2021-06-29
【Leetcode刷题篇】leetcode337 打家劫舍III 2021-06-29
【Leetcode刷题篇】leetcode4 寻找两个正序数组的中位数 2021-06-29
【Leetcode刷题篇】leetcode316 去除重复字母 2021-06-29
【Leetcode刷题篇】leetcode1081 不同字符的最小子序列 2021-06-29
【面试篇】Java网络编程与IO流体系 2021-06-29
【大话Mysql面试】-Mysql的索引为什么要使用B+树,而不是B树,红黑树等之类? 2021-06-29
【大话Mysql面试】-如何通俗易懂的了解Mysql的索引最左前缀匹配原则 2021-06-29
【大话Mysql面试】-MYSQL的两种存储引擎MyISAM与InnoDB的区别是什么? 2019-04-26
【大话Mysql面试】-InnoDB可重复读隔离级别下如何避免幻读?MVCC和next-key是什么 2019-04-26
【大话Mysql面试】-Mysql如何恢复数据?如何进行主从复制?Binlog日志到底是什么? 2019-04-26
理解String.intern()和String类常量池疑难解析例子 2019-04-26
python flask打造前后端分离的口罩检测 2019-04-26
【大话Mysql面试】-MySQL基础知识 2019-04-26
【大话Mysql面试】-MySQL数据类型有哪些 2019-04-26
【大话Mysql面试】-MySQL数据引擎 2019-04-26
【大话Mysql面试】-常见SQL语句书写 2019-04-26