编程 如何通过封装自定义Hook来实现对localStorage的响应式监听,解决了在更改时区时相关组件无法实时更新的问题

2024-11-19 03:08:13 +0800 CST views 514

响应式监听 localStorage 存储?封装个自定义 Hook 不就好了!

背景

项目中有一个全局更改时区的组件,同时还有一个局部更改时区的组件。需求是当更改时区时,所有相关组件能够实时联动并更新。

其实每次设置完时区的数据之后是存储在前端的 localStorage 中,组件也是从 localStorage 中获取默认值并显示。如果当前页面不刷新,时间组件就无法更新到最新的 localStorage 数据。那么,如何让 localStorage 存储的数据变成响应式呢?

实现

为了解决这一问题,我们可以编写一个公共的自定义 Hook,这样不仅仅是时区数据,其他需要监听 localStorage 变化的数据也可以复用这一逻辑。

失败的案例 1

一开始尝试使用 useEffect 监听 localStorage 变化:

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

测试结果失败。原因是 localStorage.getItem('timezone') 在每次渲染时都会重新计算,导致无法正确监听到变化。

失败的案例 2

接下来尝试使用 windowstorage 事件来监听 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;

然而,这种方式也失败了,因为 storage 事件只能监听同源的两个页面之间的 localStorage 变化,无法监听同一个页面的变化。

成功的案例

最终的解决方案是重写 localStorage.setItem 方法,通过触发自定义事件来实现响应式监听。

import { useState, useEffect } from "react";

function useRefreshLocalStorage(localStorage_key) {
  if (!localStorage_key || typeof localStorage_key !== "string") {
    return [null];
  }
  
  const [storageValue, setStorageValue] = useState(localStorage.getItem(localStorage_key));

  useEffect(() => {
    const originalSetItem = localStorage.setItem;

    localStorage.setItem = function(key, newValue) {
      const setItemEvent = new CustomEvent("setItemEvent", { detail: { key, newValue } });
      window.dispatchEvent(setItemEvent);
      originalSetItem.apply(this, [key, newValue]);
    };

    const handleSetItemEvent = (event) => {
      if (event.detail.key === localStorage_key) {
        setStorageValue(event.detail.newValue);
      }
    };

    window.addEventListener("setItemEvent", handleSetItemEvent);

    return () => {
      window.removeEventListener("setItemEvent", handleSetItemEvent);
      localStorage.setItem = originalSetItem;
    };
  }, [localStorage_key]);

  return [storageValue];
}

export default useRefreshLocalStorage;

使用示例

封装一个用于管理时区数据的自定义 Hook:

// 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 来存储数据,因此我们通过封装自定义 Hook 实现了 localStorage 数据的响应式更新。


复制全文 生成海报 前端开发 React 状态管理 Hooks

推荐文章

使用 Git 制作升级包
2024-11-19 02:19:48 +0800 CST
Rust async/await 异步运行时
2024-11-18 19:04:17 +0800 CST
JavaScript设计模式:适配器模式
2024-11-18 17:51:43 +0800 CST
Go语言SQL操作实战
2024-11-18 19:30:51 +0800 CST
Vue 中如何处理父子组件通信?
2024-11-17 04:35:13 +0800 CST
Nginx 如何防止 DDoS 攻击
2024-11-18 21:51:48 +0800 CST
基于Flask实现后台权限管理系统
2024-11-19 09:53:09 +0800 CST
Go中使用依赖注入的实用技巧
2024-11-19 00:24:20 +0800 CST
Dropzone.js实现文件拖放上传功能
2024-11-18 18:28:02 +0800 CST
Vue 3 是如何实现更好的性能的?
2024-11-19 09:06:25 +0800 CST
java MySQL如何获取唯一订单编号?
2024-11-18 18:51:44 +0800 CST
软件定制开发流程
2024-11-19 05:52:28 +0800 CST
Vue 3 中的 Fragments 是什么?
2024-11-17 17:05:46 +0800 CST
15 个你应该了解的有用 CSS 属性
2024-11-18 15:24:50 +0800 CST
Go 并发利器 WaitGroup
2024-11-19 02:51:18 +0800 CST
7种Go语言生成唯一ID的实用方法
2024-11-19 05:22:50 +0800 CST
Vue3中的v-bind指令有什么新特性?
2024-11-18 14:58:47 +0800 CST
JavaScript 流程控制
2024-11-19 05:14:38 +0800 CST
MySQL数据库的36条军规
2024-11-18 16:46:25 +0800 CST
程序员茄子在线接单