应该写个公共的方法,不仅仅时区数据能用,万一后边其他数据也能用。项目是 React 项目,那就写个 hook,怎么才能让 localStorage 数据变成响应式呢?监听?
useEffect(()=>{ console.log(11111, localStorage.getItem('timezone')) },[localStorage.getItem('timezone')])
得到的测试结果肯定是失败的,但是为啥失败?我们也应该知道一下。查了资料说,使用 localStorage.getItem('timezone') 作为依赖项会导致每次渲染都重新计算依赖项,这不是正确的做法。
如果你的一些依赖项是组件内部定义的对象或函数,则存在这样的风险,即它们将 导致 Effect 过多地重新运行。要解决这个问题,请删除不必要的 对象[和 函数依赖项。你还可以 抽离状态更新和 非响应式的逻辑 到 Effect 之外。
function ChatRoom({ roomId }) { const [message, setMessage] = useState(''); function createOptions() { // 🚩 此函数在每次重新渲染都从头开始创建 return { serverUrl: serverUrl, roomId: roomId }; } // 堆代码 duidaima.com useEffect(() => { const options = createOptions(); // 它在 Effect 中被使用 const connection = createConnection(); connection.connect(); return () => connection.disconnect(); }, [createOptions]); // 🚩 因此,此依赖项在每次重新渲染都是不同的 // ... }失败的案例 2
// 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_key if (event.detail.key === localStorage_key) { // 更新状态变量 storageValue const 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.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) }测试结果必须是成功的啊!!!