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

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

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

推荐文章

Graphene:一个无敌的 Python 库!
2024-11-19 04:32:49 +0800 CST
纯CSS实现3D云动画效果
2024-11-18 18:48:05 +0800 CST
PostgreSQL日常运维命令总结分享
2024-11-18 06:58:22 +0800 CST
Vue3中的JSX有什么不同?
2024-11-18 16:18:49 +0800 CST
手机导航效果
2024-11-19 07:53:16 +0800 CST
Vue 3 中的 Fragments 是什么?
2024-11-17 17:05:46 +0800 CST
filecmp,一个Python中非常有用的库
2024-11-19 03:23:11 +0800 CST
在 Docker 中部署 Vue 开发环境
2024-11-18 15:04:41 +0800 CST
H5端向App端通信(Uniapp 必会)
2025-02-20 10:32:26 +0800 CST
Claude:审美炸裂的网页生成工具
2024-11-19 09:38:41 +0800 CST
js一键生成随机颜色:randomColor
2024-11-18 10:13:44 +0800 CST
使用 Vue3 和 Axios 实现 CRUD 操作
2024-11-19 01:57:50 +0800 CST
java MySQL如何获取唯一订单编号?
2024-11-18 18:51:44 +0800 CST
Golang 中应该知道的 defer 知识
2024-11-18 13:18:56 +0800 CST
Elasticsearch 聚合和分析
2024-11-19 06:44:08 +0800 CST
程序员茄子在线接单