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

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

响应式监听 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

推荐文章

支付页面html收银台
2025-03-06 14:59:20 +0800 CST
Elasticsearch 文档操作
2024-11-18 12:36:01 +0800 CST
Golang - 使用 GoFakeIt 生成 Mock 数据
2024-11-18 15:51:22 +0800 CST
Vue3中如何处理组件间的动画?
2024-11-17 04:54:49 +0800 CST
38个实用的JavaScript技巧
2024-11-19 07:42:44 +0800 CST
Web浏览器的定时器问题思考
2024-11-18 22:19:55 +0800 CST
SQL常用优化的技巧
2024-11-18 15:56:06 +0800 CST
如何在Vue 3中使用Ref访问DOM元素
2024-11-17 04:22:38 +0800 CST
Vue3中的响应式原理是什么?
2024-11-19 09:43:12 +0800 CST
CSS 媒体查询
2024-11-18 13:42:46 +0800 CST
宝塔面板 Nginx 服务管理命令
2024-11-18 17:26:26 +0800 CST
PHP 代码功能与使用说明
2024-11-18 23:08:44 +0800 CST
Vue3中如何进行错误处理?
2024-11-18 05:17:47 +0800 CST
Nginx 实操指南:从入门到精通
2024-11-19 04:16:19 +0800 CST
Go 并发利器 WaitGroup
2024-11-19 02:51:18 +0800 CST
实用MySQL函数
2024-11-19 03:00:12 +0800 CST
用 Rust 玩转 Google Sheets API
2024-11-19 02:36:20 +0800 CST
向满屏的 Import 语句说再见!
2024-11-18 12:20:51 +0800 CST
Vue3中的事件处理方式有何变化?
2024-11-17 17:10:29 +0800 CST
Elasticsearch 的索引操作
2024-11-19 03:41:41 +0800 CST
程序员茄子在线接单