如何让 localStorage 数据实现实时响应

news/2024/12/16 13:02:10/文章来源:https://www.cnblogs.com/infinilabs/p/18609847

重大事项

📣 :重大事项提前通知!快来围观,不容错过!

极限科技 一直致力于为开发者和企业提供优质的开源工具,提升整个技术生态的活力。除了维护国内最流行的分词器 analysis-ikanalysis-pinyin,也在不断推动更多高质量开源产品的诞生。

在极限科技成立三周年之际,公司宣布以下产品和工具已全面开源:

  • INFINI Framework
  • INFINI Gateway
  • INFINI Console
  • INFINI Agent
  • INFINI Loadgen
  • INFINI Coco AI

以上开源软件都可以在 Github 上面找到: https://github.com/infinilabs

希望大家都能给个免费的 Star🌟 支持一下!!!

背景

在开发公司项目 INFINI Cloud(暂未开源,敬请期待。 不过此次开源的同类项目有 INFINI Console)的时候,该项目上有个更改时区的全局组件,同时还有一个可以更改时区的局部组件,想让更改时区的时候能联动起来,实时响应起来

image.png

Tip:如果有人对该时间组件感兴趣,可以移步 https://github.com/infinilabs/ui-common,同时也希望收到您 Star🌟 支持,也希望和大家一起共建。

其实每次设置完时区的数据之后是存在了前端的 localStorage 里边,时间组件里边也是从 localStorage 拿去默认值来回显。如果当前页面不刷新,那么时间组件就不能更新到最新的 localStorage 数据。

怎么才能让 localStorage 存储的数也变成响应式呢?

实现

  1. 应该写个公共的方法,不仅仅时区数据能用,万一后边其他数据也能用。
  2. 项目是 React 项目,那就写个 hook
  3. 怎么才能让 localStorage 数据变成响应式呢?监听?

失败的案例 1

首先想到的是按照下边这种方式做,

useEffect(() => {console.log(11111, localStorage.getItem("timezone"));
}, [localStorage.getItem("timezone")]);

得到的测试结果肯定是失败的,但是为啥失败?我们也应该知道一下。查了资料说,使用 localStorage.getItem('timezone') 作为依赖项会导致每次渲染都重新计算依赖项,这不是正确的做法。

具体看一下官方文档:useEffect(setup, dependencies?)

在此说一下第二个参数 dependencies

可选 dependenciessetup 代码中引用的所有响应式值的列表。响应式值包括 props、state 以及所有直接在组件内部声明的变量和函数。如果你的代码检查工具 配置了 React,那么它将验证是否每个响应式值都被正确地指定为一个依赖项。依赖项列表的元素数量必须是固定的,并且必须像 [dep1, dep2, dep3] 这样内联编写。React 将使用 Object.is 来比较每个依赖项和它先前的值。如果省略此参数,则在每次重新渲染组件之后,将重新运行 Effect 函数。

  • 如果你的一些依赖项是组件内部定义的对象或函数,则存在这样的风险,即它们将 导致 Effect 过多地重新运行。要解决这个问题,请删除不必要的 对象 和 函数 依赖项。你还可以 抽离状态更新 和 非响应式的逻辑 到 Effect 之外。

如果你的 Effect 依赖于在渲染期间创建的对象或函数,则它可能会频繁运行。例如,此 Effect 在每次渲染后重新连接,因为 createOptions 函数 在每次渲染时都不同:

function ChatRoom({ roomId }) {const [message, setMessage] = useState("");function createOptions() {// 🚩 此函数在每次重新渲染都从头开始创建return {serverUrl: serverUrl,roomId: roomId,};}useEffect(() => {const options = createOptions(); // 它在 Effect 中被使用const connection = createConnection();connection.connect();return () => connection.disconnect();}, [createOptions]); // 🚩 因此,此依赖项在每次重新渲染都是不同的// ...
}

失败的案例 2

一开始能想到的是监听,那就用 window 上监听事件。

在 React 应用中监听 localStorage 的变化,可以使用 window 对象的 storage 事件。这个事件在同一域名的不同文档之间共享,当某个文档修改 localStorage 时,其他文档会收到通知。

写代码...

// useRefreshLocalStorage.js
import { useState, useEffect } from "react";const useRefreshLocalStorage = (key) => {const [storageValue, setStorageValue] = useState(localStorage.getItem(key));useEffect(() => {const handleStorageChange = (event) => {if (event.key === key) {setStorageValue(event.newValue);}};window.addEventListener("storage", handleStorageChange);return () => {window.removeEventListener("storage", handleStorageChange);};}, [key]);return [storageValue];
};export default useRefreshLocalStorage;

使用方式:

// useTimezone.js
import { useState, useEffect } from "react";import { getTimezone, timezoneKey } from "@/utils/utils";
import useRefreshLocalStorage from "./useRefreshLocalStorage";function useTimezone() {const [TimeZone, setTimeZone] = useState(() => getTimezone());const [storageValue] = useRefreshLocalStorage(timezoneKey);useEffect(() => {setTimeZone(() => getTimezone());}, [storageValue]);return [TimeZone];
}export default useTimezone;

经过测试,失败了,没有效果!!!那到底怎么回事呢?哪里出现问题了?查阅资料经过思考,可能出现的问题的原因有:只能监听同源的两个页面之间的 storage 变更,没法监听同一个页面的变更。

成功的案例

import { useState, useEffect } from "react";// 自定义 Hook,用于监听 localStorage 中指定键的变化
function useRefreshLocalStorage(localStorage_key) {// 检查 localStorage_key 是否有效if (!localStorage_key || typeof localStorage_key !== "string") {return [null];}// 创建一个状态变量来保存 localStorage 中的值const [storageValue, setStorageValue] = useState(localStorage.getItem(localStorage_key));useEffect(() => {// 保存原始的 localStorage.setItem 方法const originalSetItem = localStorage.setItem;// 重写 localStorage.setItem 方法,添加事件触发逻辑localStorage.setItem = function (key, newValue) {// 创建一个自定义事件,用于通知 localStorage 的变化const setItemEvent = new CustomEvent("setItemEvent", {detail: { key, newValue },});// 触发自定义事件window.dispatchEvent(setItemEvent);// 调用原始的 localStorage.setItem 方法originalSetItem.apply(this, [key, newValue]);};// 事件处理函数,用于处理自定义事件const handleSetItemEvent = (event) => {const customEvent = event;// 检查事件的键是否是我们关心的 localStorage_keyif (event.detail.key === localStorage_key) {// 更新状态变量 storageValueconst updatedValue = customEvent.detail.newValue;setStorageValue(updatedValue);}};// 添加自定义事件的监听器window.addEventListener("setItemEvent", handleSetItemEvent);// 清除事件监听器和还原原始方法return () => {// 移除自定义事件监听器window.removeEventListener("setItemEvent", handleSetItemEvent);// 还原原始的 localStorage.setItem 方法localStorage.setItem = originalSetItem;};// 依赖数组,只在 localStorage_key 变化时重新运行 useEffect}, [localStorage_key]);// 返回当前的 storageValue// 为啥没有返回 setStorageValue ?// 因为想让用户直接操作自己真实的 “setValue” 方法,这里只做一个只读。return [storageValue];
}export default useRefreshLocalStorage;

具体的实现步骤如上,每一步也加上了注释。

接下来就是测试了,

useTimezone 针对 timezone 数据统一封装,

// useTimezone.js
import { useState, useEffect } from "react";import { getTimezone, timezoneKey } from "@/utils/utils";
import useRefreshLocalStorage from "./useRefreshLocalStorage";function useTimezone() {const [TimeZone, setTimeZone] = useState(() => getTimezone());const [storageValue] = useRefreshLocalStorage(timezoneKey);useEffect(() => {setTimeZone(() => getTimezone());}, [storageValue]);return [TimeZone];
}export default useTimezone;

具体的业务页面组件中使用,

// 页面中
// ...
import useTimezone from "@/hooks/useTimezone";export default (props) => {// ...const [TimeZone] = useTimezone();useEffect(()=>{console.log(11111, TimeZone)},[TimeZone)
}

测试结果必须是成功的啊!!!

小结

其实想要做到该效果,用全局 store 状态管理也能做到,条条大路通罗马嘛!不过本次需求由于历史原因一直使用的是 localStorage ,索性就想着 如何让 localStorage 存储变为响应式 ?

不知道大家还有什么更好的方法吗?

作者:Rain9,极限科技(INFINI Labs) 高级前端开发工程师。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/853744.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

loadSend:免费开源局域网数据传输工具 全平台支持 传输工具

前言 不同系统的电脑、手机,文件传输有没有简单一点的方法? 手机是iPhone,电脑是Windows,如何更快捷传输文件呢? 我们最常用和用得最多的文件传输工具可能就是微信以及 QQ 了吧! 其实,如果只是在局域网内,用微信这一类聊天工具来传输文件并不算特别合适,除了可能存在的…

焦作本地在线教育系统价格

近年来,在线教育的普及使得传统教培模式受到了新的挑战。无论是大型教育集团,还是地方性的教培中心,纷纷转型线上,寻求更为灵活的盈利方式和发展机遇。在众多竞争者中,了解并运用合适的在线教育系统对维持市场地位至关重要。图源 凸知@www.tuzhi.ltd对于许多像焦作地区教育…

项目管理看板:实现任务透明化与实时跟踪

一、项目管理看板的定义与背景 1.1 什么是项目管理看板? 项目管理看板(Project Management Kanban)是一种可视化的任务管理工具,旨在帮助团队或项目管理者清晰地展示项目任务的状态,并对任务的进展进行实时跟踪。看板通常分为若干列,每一列代表任务的不同阶段(例如:待办…

实验六 C语言结构体、枚举应用编程

实验任务1 task1.c1 // P286例8.172 // 对教材示例代码作了微调,把输出学生信息单独编写成一个函数模块3 // 打印不及格学生信息、打印所有学生信息均调用该模块实现4 5 #include <stdio.h>6 #include <string.h> 7 #define N 3 // 运行程序输入测试时,可…

H7-TOOL自制Flash读写保护算法系列,为凌欧LKS32MC45x/MC05x/MC08x制作使能和解除算法,支持在线烧录和脱机烧录使用2024-12-15

说明:很多IC厂家仅发布了内部Flash算法文件,并没有提供读写保护算法文件,也就是选项字节算法文件,需要我们制作。 实际上当前已经发布的TOOL版本,已经自制很多了,比如已经支持的兆易创新大部分型号,新唐的大部分型号等。但是依然有些厂家还没自制,所以陆续开始为这些厂…

Jenkins拉取GitLab代码

Jenkins、GitLab、Jenkins拉取GitLab代码Jenkins从GitLab中拉取代码 1.在Jenkins主机上生成ssh密钥 [root@jenkins gitrepo]# ssh-keygen -t ed25519 Generating public/private ed25519 key pair. Enter file in which to save the key (/root/.ssh/id_ed25519): Enter passph…

Kenkins拉取GitLab代码

Jenkins、GitLab、Jenkins拉取GitLab代码Jenkins从GitLab中拉取代码 1.在Jenkins主机上生成ssh密钥 [root@jenkins gitrepo]# ssh-keygen -t ed25519 Generating public/private ed25519 key pair. Enter file in which to save the key (/root/.ssh/id_ed25519): Enter passph…

Kenkins拉取GitLab代码 - 副本

Jenkins、GitLab、Jenkins拉取GitLab代码Jenkins从GitLab中拉取代码 1.在Jenkins主机上生成ssh密钥 [root@jenkins gitrepo]# ssh-keygen -t ed25519 Generating public/private ed25519 key pair. Enter file in which to save the key (/root/.ssh/id_ed25519): Enter passph…

2024《毒液3》最后一舞 Venom: The Last Dance 【内封简英双语字幕】电影百度云/夸克迅雷UC网盘资源链接下载

导演凯莉马塞尔 主演汤姆哈迪 / 切瓦特埃加福 / 朱诺坦普尔 / 瑞斯伊凡斯 / 斯蒂芬格拉汉姆 / 佩吉陆 / 安迪瑟金斯 / 克拉克巴茨科 / 阿兰娜乌巴赫 / 克里斯托费尔南德斯 / 杰瑞德亚伯拉汉姆森 / 哈拉芬利 / 达什麦克劳德 / 瑞德斯科特 / 杰克布雷迪 / 伊沃南迪 / 杰克阿林 / …

2024《毒液3》最后一舞 Venom: The Last Dance 【内封简英双语字幕】电影(含网盘链接)

导演凯莉马塞尔 主演汤姆哈迪 / 切瓦特埃加福 / 朱诺坦普尔 / 瑞斯伊凡斯 / 斯蒂芬格拉汉姆 / 佩吉陆 / 安迪瑟金斯 / 克拉克巴茨科 / 阿兰娜乌巴赫 / 克里斯托费尔南德斯 / 杰瑞德亚伯拉汉姆森 / 哈拉芬利 / 达什麦克劳德 / 瑞德斯科特 / 杰克布雷迪 / 伊沃南迪 / 杰克阿林 / …

如何计算多分类情况下的敏感性指标

1. 混淆矩阵cm,其中矩阵元素 cm[i][j] 表示真实标签为第 i 类且被预测为第 j 类的样本个数。 2. TP, TN, FP, FN(以类II为例)TP: 实际为正,预测为正(最中间这一格)FN:实际为真,预测为假(中间这一排里面,挖去中间一格,剩下的格子)FP:实际为假,预测为真(中间这一列…

巴黎之旅的美好回忆

ssssss在巴黎的日子里,我们经历了许多难忘的事情...