useEffect 不监听 localStorage

问题描述 投票:0回答:5

我正在制作一个身份验证系统,在后端将我重定向到前端页面后,我正在向 userData 发出 API 请求,并将该数据保存到 localStorage。然后我尝试加载 Spinner 或 UserInfo。

我正在尝试使用 useEffect 监听 localStorage 值,但登录后我得到“未定义”。当 localStorage 值更新时,useEffect 不会再次运行,Spinner 会永远旋转。

我尝试这样做:

JSON.parse(localStorage.getItem('userData'))
,但后来我得到了 useEffect 无限循环。

只有当我刷新页面时,我的 localStorage 值才会出现,并且我可以显示它而不是 Spinner。

我做错了什么?

也许有更好的方法来加载准备好的用户数据?

我正在尝试以正确的方式更新 DOM?

感谢您的回答;)

import React, { useState, useEffect } from 'react';
import { Spinner } from '../../atoms';
import { Navbar } from '../../organisms/';
import { getUserData } from '../../../helpers/functions';

const Main = () => {
  const [userData, setUserData] = useState();
  useEffect(() => {
    setUserData(localStorage.getItem('userData'));
  }, [localStorage.getItem('userData')]);

  return <>{userData ? <Navbar /> : <Spinner />}</>;
};

export default Main;
javascript node.js reactjs react-hooks local-storage
5个回答
45
投票

这里最好添加localstorage的事件监听器。

useEffect(() => {
  function checkUserData() {
    const item = localStorage.getItem('userData')

    if (item) {
      setUserData(item)
    }
  }

  window.addEventListener('storage', checkUserData)

  return () => {
    window.removeEventListener('storage', checkUserData)
  }
}, [])

11
投票

“存储”事件的事件侦听器在同一页面中不起作用

当存储区域被占用时,Window接口的存储事件会被触发 (localStorage) 已在另一个文档的上下文中进行了修改。

https://developer.mozilla.org/en-US/docs/Web/API/Window/storage_event


5
投票

解决方案是使用这个结构:

useEffect(() => {
    window.addEventListener("storage", () => {
      // When storage changes refetch
      refetch();
    });

    return () => {
      // When the component unmounts remove the event listener
      window.removeEventListener("storage");
    };
}, []);

0
投票

React 18 的工作版本

useSyncExternalStore
:

import { useCallback, useSyncExternalStore } from "react";

interface UseLocalStorage<D> {
  data: D | undefined,
  setData: (data: D) => void,
  clear: () => void,
}
const EVENT_TYPE = "localUpdated";
class LocalStorageEvent extends Event {
  constructor(readonly key: string) {
    super(EVENT_TYPE, { bubbles: true });
  }
}

export function useLocalStorage<D>(key: string): UseLocalStorage<D> {
  const getSnapshot = useCallback(() => readLocalStorage<D>(key), [key]);
  const subscribe = useCallback((onChange: () => void) => subscribeToLocalStorage(onChange, key), [key]);
  const data = useSyncExternalStore<D | undefined>(subscribe, getSnapshot);

  const setData = useCallback((data: D) => writeToLocalStorage(key, data), [key]);
  const clear = useCallback(() => clearLocalStorage(key), [key]);
  return { data, setData, clear };
}

function subscribeToLocalStorage(onStorageChange: () => void, key: string) {
  function listener(event: Event) {
    if (event instanceof LocalStorageEvent && event.key === key) {
      onStorageChange();
    }
  }

  window.addEventListener(EVENT_TYPE, listener);
  return () => window.removeEventListener(EVENT_TYPE, listener);
}


function readLocalStorage<D>(key: string): D | undefined {
  const value = localStorage.getItem(key);
  return value ? JSON.parse(value) : undefined;
}

function writeToLocalStorage<D>(key: string, value: D): void {
  const json = JSON.stringify(value);
  localStorage.setItem(key, json);
  document.dispatchEvent(new LocalStorageEvent(key));
}

function clearLocalStorage(key: string): void {
  localStorage.removeItem(key);
  document.dispatchEvent(new LocalStorageEvent(key));
}


-2
投票
  • “也许有更好的方法来加载准备好的用户数据?”

您可以直接将值计算到 localStorage 中,而不是传递到状态。

const Main = () => {
  if (localStage.getItem('userData')) {
    return (<Navbar />);
  }
  else {
    return (<Spinner />);
  }
};

如果需要在更多组件中检索 userData,请评估 Redux 在您的应用程序中的实现,这可以消除 localStorage 的使用,但这当然取决于您的需求。

© www.soinside.com 2019 - 2024. All rights reserved.