redux 时间旅行,你值得拥有!

2023-10-29

啥叫时间旅行?

顾名思义,就是可以随时穿越到以前和未来,让应用程序切换到任意时间的状态。我们都知道,一般应用状态都很复杂,创建、维护、修改和弄明白有哪些行为会影响状态都不是一件容易的事儿。

redux 的解决方案

整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。并使用纯函数计算下一个应用程序状态(不允许其他途径对 state 进行修改)。这些特征使 Redux 成为了一个可预测 的状态容器,这意味着如果给定一个特定应用程序状态和一个特定操作,那么应用程序的下一个状态将始终完全相同。这种可预测性使得实现时间旅行变得很容易。redux 也相应的开发了一个带时间旅行的开发者工具redux-devtools

就是上面这个东西。下面就让我们跟随例子一起来了解下 redux 时间旅行的工作原理。

阅读要求

  • react 基础
  • redux 基础,明白 action,reducer,state 的关系。明白 combineReducer 的原理。

开始

项目地址:(github)[github.com/wuyafeiJS/r…]

预览:

既然我们要实现时间旅行,那么第一步我们需要一个对象来记录每一次状态: stateHistory.js

export default {
  past: [],
  futrue: [],
  present: undefined,
  gotoState(i) {
    const index = i * 1;
    const allState = [...this.past, this.present, ...this.futrue];
    this.present = allState[index];
    this.past = allState.slice(0, index);
    this.futrue = allState.slice(index + 1, allState.length);
  }
};
复制代码

我们把状态分为三个时间段:过去,现在(只有一个状态),将来。gotoState 函数则是用来做时间旅行的,他的实现方式就是整合所有状态 allState,重新分配,present 前面是 past,后面是 future。

那么我们如何去存放每一次变更的状态呢?我们需要找到一个入口,这个入口必须是每次触发状态变更都会经过的地方。而触发状态变更唯一的方式就是dispatch(action),想想,这样的地方好像只有一个地方,看过 redux 源码的同学肯定就是不陌生,那就是 combineReducer 生成的 reducers 纯函数。 combineReducer 负责整合多个 reducer,最终返回一个能够处理所有 action 的 reducers。让我们大致简单实现一下:

const combineReducer = obj => (state, action) => {
  const finalState = {};
  for (key in obj) {
    finanlState[key] = obj[key](state[key], action);
  }
  return finalState; // 全局state
};
复制代码

接下来,让我们利用函数式编程的思想加强下 reducers 的功能,让它能记录 state:reducers.js

import stateHistory from './stateHistory';// 引入我们之前声明的history对象

// 原本我们是这样返回reducers的
export default combineReducers({
    books: fetchReducer,
    displayMode: bookDisplayReducer,
    currentStatus: statusReducer,
    topic: topicReducer
})
// 改造后如下:
export default history(
  combineReducers({
    books: fetchReducer,
    displayMode: bookDisplayReducer,
    currentStatus: statusReducer,
    topic: topicReducer
  })
);
// 我们用history包裹combineReducer,history实现如下
const history = reducers => (state, aciton) => {
  switch (action.type) {
    case 'UNDO': // 后退
      stateHistory.undo();
      break;
    case 'REDO': // 前进
      stateHistory.redo();
      break;
    case 'GOTO': // 定点指向
      stateHistory.gotoState(action.stateIndex);
      break;
    default:
      const newState = reducer(state, action);
      stateHistory.push(newState);// 每次dipatch(action)都会像将状态保存到stateHistory
  }
  return stateHistory.present; // 返回当前状态
}
复制代码

完善下stateHistory.js

export default {
  ...

  hasRecord(type) {// 查询是否有过去或者将来的状态
    return this[type].length > 0;
  },
  hasPresent() { // 查询是否有现在的状态
    return this.present !== undefined;
  },
  setPresent(state) {
    this.present = state;
  },
  movePresentToPast() {
    this.past.push(this.present);
  },
  push(currentState) { // 将状态都保存,并更新当前状态
    if (this.hasPresent()) {
      this.past.push(this.present);
    }
    this.setPresent(currentState);
  },
  getIndex() { // 获取当前状态index
    return this.past.length;
  },
  undo() { // 后退
    if (this.hasRecord('past')) {
      this.gotoState(this.getIndex() - 1);
    }
  },
  redo() { // 前进
    if (this.hasRecord('futrue')) {
      this.gotoState(this.getIndex() + 1);
    }
  },
  ...
};
复制代码

配置 action:actions.js

...
export const redo = () => ({
  type: 'REDO'
});

export const undo = () => ({
  type: 'UNDO'
});

export const gotoState = stateIndex => ({
  type: 'GOTO',
  stateIndex
});
复制代码

准备工作都已经做完,接下来咱们直接在 react 组件内加上触发代码即可components/History.js

const History = ({ past, futrue, present, redo, undo, gotoState }) => {
  const styles = {
    container: {
      marginLeft: '20px',
      cursor: 'pointer'
    },

    link: { textDecoration: 'none' },
    input: { cursor: 'pointer' }
  };
  const RightArrow = () => (
    // 前进
    <a href="#" style={styles.link} onClick={() => redo()}>
      &#8594;
    </a>
  );

  const LeftArrow = () => (
    // 后退
    <a href="#" style={styles.link} onClick={() => undo()}>
      &#8592;
    </a>
  );
  const max = () =>
    (past ? past.length : 0) +
    (present ? 1 : 0) +
    (futrue ? futrue.length : 0) -
    1;
  const value = () => (past ? past.length : 0);
  return (
    <span>
      <input
        type="range"
        min={0}
        max={max()}
        value={value()}
        onChange={e => {
          gotoState(e.target.value);
        }}
        style={styles.input}
      />
      {past && past.length > 0 ? <LeftArrow /> : null}
      {futrue && futrue.length > 0 ? <RightArrow /> : null}
    </span>
  );
};
复制代码

以上!希望对大家理解 redux 有所帮助。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

redux 时间旅行,你值得拥有! 的相关文章

  • 如何检测浏览器是否支持自定义元素

    我正在查看 Modernizr 它应该有助于功能检测 这应该可以帮助确定您的网站是否与给定的 Web 浏览器兼容 但我没有看到任何表明我可以使用它来检测自定义 HTML 的内容我们在内容中创建和定义的元素 如果不是 Modernizr 我如
  • 在 javascript/jquery 中将光标更改为等待

    当调用函数时 如何让光标更改为此加载图标以及如何将其更改回 javascript jquery 中的普通光标 在你的 jQuery 中使用 body css cursor progress 然后又恢复正常 body css cursor d
  • 关闭选项卡时要求确认[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 当我在某些浏览器上关闭页面时 我希望出现一个消息框 并询问我是否真的要关闭页面 有两个按钮 如果我单击No那么这个标签就不会被关闭 我怎样
  • 从数据 URI 解码 QR 码

    我尝试从数据 uri 中解码二维码 var dataUri data image gif base64 R0lGODdh9gD2AIAAAAAAAP ywAAAAA9gD2AAAC decodeQrCode dataUri cb 我已经尝试
  • 使用 useReducers 调度函数发送多个操作?

    使用时是否可以通过调度函数发送多个动作useReducer挂钩反应 我尝试向它传递一组操作 但这会引发未处理的运行时异常 明确地说 通常会有一个初始状态对象和一个减速器 如下所示 const initialState message1 nu
  • 使用 jQuery/JS 打开时使
    标签的内容具有动画效果

    我只想要 HTML5 的内容details标记为 滑行 动画打开 而不是仅仅弹出打开 立即出现 这可以用 jQuery Javascript 实现吗 Fiddle http jsfiddle net 9h4Hq HTML
  • 检查 JavaScript 字符串是否为 URL

    JavaScript 有没有办法检查字符串是否是 URL 正则表达式被排除在外 因为 URL 很可能是这样写的stackoverflow 也就是说它可能没有 com www or http 如果你想检查一个字符串是否是有效的 HTTP UR
  • 使用 Angular 下载具有动态 src 的脚本

    Angular 提供了通过动态名称动态加载模板的方法ng include 该部分中的内联 JS 和 CSS 可以正常加载 但没有一个好的方法来下载带有动态 url 的脚本 我们需要下载脚本 相对于调用它们的 html 部分的路径 即我们有一
  • 如何抑制窗口鼠标滚轮滚动...?

    我正在开发嵌入页面中的画布应用程序 我有它 因此您可以使用鼠标滚轮放大绘图 但不幸的是 这会滚动页面 因为它是文章的一部分 当我在 dom 元素上滚动鼠标滚轮时 是否可以阻止鼠标滚轮在窗口上滚动 附加鼠标滚轮 不是 Gecko DOMMou
  • 除了更改标题之外,如何在 Firefox 中强制另存为对话框?

    有没有办法在 ff 中强制打开 www example com example pdf 的另存为对话框 我无法更改标题 如果您可以将文件以 Base64 格式输出到客户端 则可以使用 data uri 进行下载 location href
  • 为什么是 javascript:history.go(-1);无法在移动设备上工作?

    首先 一些背景 我有一个向用户呈现搜索页面 html 表单 的应用程序 填写标准并单击 搜索 按钮后 结果将显示在标准部分下方 在结果列表中 您可以通过单击将您带到新页面的链接来查看单个结果的详细信息 在详细信息页面中 我添加了一个 返回结
  • 使用 KnockoutJs 映射插件进行递归模板化

    我正在尝试使用以下方法在树上进行递归模板化ko映射 插入 http knockoutjs com documentation plugins mapping html 但我无法渲染它 除非我定义separate每个级别的模板 在以下情况下
  • 跟踪用户何时点击浏览器上的后退按钮

    是否可以检测用户何时单击浏览器的后退按钮 我有一个 Ajax 应用程序 如果我可以检测到用户何时单击后退按钮 我可以显示适当的数据 任何使用 PHP JavaScript 的解决方案都是优选的 任何语言的解决方案都可以 只需要我可以翻译成
  • 在javascript中解析json - 长数字被四舍五入

    我需要解析一个包含长数字的 json 在 java servlet 中生成 问题是长数字被四舍五入 当执行这段代码时 var s x 6855337641038665531 var obj JSON parse s alert obj x
  • Babel 7 Jest Core JS“TypeError:wks不是函数”

    将我的项目升级到 Babel 7 后 通过 Jest 运行测试会抛出以下错误 测试在 Babel 6 中运行没有任何问题 但在 Babel 7 中失败并出现以下错误 TypeError wks is not a function at Ob
  • 如何在react-native中获取Text组件的onPress值

    我是一名新的 React Native 开发人员 我想使用 onPress 获取 Text 组件的值并将其传递给函数
  • Electron - 为什么在关闭事件时将 BrowserWindow 实例设置为 null

    The 电子文档 https electronjs org docs api browser window 提供以下代码示例来创建新窗口 const BrowserWindow require electron let win new Br
  • 有没有办法阻止 prettier / prettier-now 将函数参数分解为新行

    当使用 prettier prettier now 在保存时进行格式化时 当一个函数包装另一个函数时 它会中断到一个新行 我想知道是否有办法阻止这种行为 例如 期望的输出 app get campgrounds id catchAsync
  • 条件在反应本机生产中失败,但在开发中有效

    我创建了一个反应本机应用程序 我需要通过它进行比较 如果属实 就会执行死刑 问题是 该条件适用于 React Native 开发模式 而不适用于 React Native 生产版本 我使用 firebase 作为数据库 也使用 redux
  • fullCalendar 未显示正确的结束日期

    我正在看调试页面 http jsbin com wukofacaxu edit js outputFullCalendar 官方网站的 我想安排一个活动时间为 22 09 2015 至 30 09 2015 dd mm yyyy 但它只显示

随机推荐

  • antlr4 Verilog2001.g4

    verilog which antlr4 antlr4 aliased to java Xmx500M cp usr local lib antlr 4 9 complete jar CLASSPATH org antlr v4 Tool
  • 子shell的理解

    创建子shell 在当前shell 使用bash 命令即可创建子shell程序 在子shell上还可以继续创建子shell 例子 使用命令 ps f 查看进程信息 程序运行后就是进程 它可以反应程序运行的状态信息 user1 localho
  • k8s.io/client-go@v0.20.2/tools/cache/reflector.go:167: Failed to watch *v1beta1.Ingress: failed to l

    原因 kubectl version v1 22 不再支持v1beta1 所以要解决这个问题需要把ingress nginx 换成最新的版本 1 0 0
  • Android10填坑适配指南,实际经验代码,持续补充

    Android10填坑适配指南 包含实际经验代码 绝不照搬翻译文档 1 Region Op相关异常 java lang IllegalArgumentException Invalid Region Op only INTERSECT an
  • HCIP datacom 821、831题库

    全部黄色底纹为错误题 绿色 全部绿色字体为正确题 关于OSPF缺省路由描述错误的是 由于OSPF路由的分级管理 Type5 7缺省路由的优先级高于Type3路由 OSPF的Router LSA中 如果其Link Type为1 则该LSA描述
  • 几行命令完成docker多网站的部署(wordpress和dedecms)

    建设目标 利用docker技术快速搭建wordpress和dedecms 完成多域名的部署工作 操作时间评估 20分钟 主要部署内容 php7 nginx myssql wordpress dedecms 主要操作过程 几个命令行 安装环境
  • IDEA学习JDBC编程连接MySQL

    IDEA连接数据库可以有两种方式 1 通过Database建立连接 连接成功后可以直接操作数据库 需要注意的是MySQL 8 版本的Driver与之前的有所不同 com mysql cj jdbc Driver 还有一点就是时区问题 全球标
  • 爽啊爽啊爽

    致五个月的兄弟连兄弟姐妹 我们来兄弟连干什么 写程序写程序 写程序 上课的时候干什么 写程序写程序 写程序 下课的时候干什么 写程序写程序 写程序 宿舍的时候干什么 写程序写程序 写程序 开心的时候干什么 写程序写程序 写程序 伤心的时候干
  • URP教务系统自动登录

    文章目录 验证码 获取验证码图片 识别验证码内容 图片降噪处理 自动登录 爬取需要的数据 这篇博客是我对 hack 进学校教务系统的一个过程总结 详细代码已经放在GitHub上 需要的自取 URP教务系统自动登录脚本 验证码 打开网站 ht
  • 内存管理408

    文章目录 一 内存管理概念 1 内存管理概念 1 1 内存管理的基本原理与要求 1 2 逻辑地址与物理地址 1 3 进程的内存映像 1 4 内存保护 1 5 内存共享 2 覆盖与交换 2 1 覆盖 2 2 交换 3 连续分配管理方式 3 1
  • 解决vue-router-active无效问题

    设置的样式中 path为 的icon始终被设置了active样式 解决办法 由于router link to的每个路由首部都存在 因此都可以激活默认路由 这也是为什么当path为 的时候始终有active样式 甚至出现了当点击其他tab按键
  • vue+element-ui+vuex实现 面包屑 + Tag多标签切换功能

    参考网址 https www cnblogs com qdhxhz p 12590324 html 建议 亲测可用 把上一篇文章一起看 从左侧菜单栏看起就不会乱了
  • java居然也能实现简单斗地主功能

    出于无聊 就想着斗地主能否通过java程序来写 后面思考了下确实可以实现 目前只实现了发牌的功能 出牌和智能出牌的代码尚未写 有兴趣的朋友可以和我一起试试看 具体代码还比较简单 具体代码如下 package com xinwei oms i
  • 2023华为OD机试真题-单词倒序(JAVA、Python、C++)

    题目描述 输入单行英文句子 里面包含英文字母 空格以及 三种标点符号 请将句子内每个单词进行倒序 并输出倒序后的语句 输入描述 输入字符串S S的长度1 N 100 输出描述 输出逆序后的字符串 补充说明 标点符号左右的空格 0 单词间空格
  • COM学习笔记8_IDispatch (调度接口) 自动化

    一般的通讯方式 客户 lt gt COM vbtl 接口 lt gt COM组件 自动化通讯方式 客户 自动化控制器 lt gt IDispatch Invoke lt gt 调度接口 或vbtl接口 lt gt 实现IDispatch接口
  • springboot整合shiro完整配置

    springboot整合shiro完整配置 springboot整合shiro的maven依赖 springboot版本为2 1 7 shiro版本为1 5 3
  • 实验四 手写数字识别的神经网络算法设计与实现

    一 实验目的 通过学习BP神经网络技术 对手写数字进行识别 基于结构的识别法及模板匹配法来提高识别率 二 实验器材 PC机 matlab软件 三 实验内容 按照BP神经网络设计方法选用两层BP网络 构造训练样本集 并构成训练所需的输入矢量和
  • 求凹多边形的视觉中心,不是质心、重心

    思路都是google上找的 思路1 效果不是很好 勉强可以 reference http stackoverflow com questions 25495560 how can i find the best place to fit a
  • linux使用mail命令发送像模像样的邮件

    代码功能 发出带标题 正文 收件人 抄送收件人和附件的邮件 代码如下 mail content mail content cur time 邮件正文存储文件 attachment attacnment cur time 附件 uuencod
  • redux 时间旅行,你值得拥有!

    啥叫时间旅行 顾名思义 就是可以随时穿越到以前和未来 让应用程序切换到任意时间的状态 我们都知道 一般应用状态都很复杂 创建 维护 修改和弄明白有哪些行为会影响状态都不是一件容易的事儿 redux 的解决方案 整个应用的 state 被储存