简体   繁体   English

React 自定义钩子 state 更改不触发重新渲染

[英]React custom hook state change not triggering re-render

I have created a custom react hook that allows users to toggle the color theme of my application (ie 'light' or 'dark'), and the state change is not causing the components that call the hook to re-render.我创建了一个自定义反应挂钩,允许用户切换我的应用程序的颜色主题(即“浅色”或“深色”),并且 state 更改不会导致调用挂钩的组件重新呈现。

The hook is called useColorTheme and it's defined as follows:该挂钩称为useColorTheme ,其定义如下:

import { useEffect } from "react";
import useLocalStorage from "./useLocalStorage";

export default function useColorTheme() {
  const [colorTheme, setColorTheme] = useLocalStorage("color-theme", "light");

  useEffect(() => {
    const className = "dark";
    const bodyClass = window.document.body.classList;

    colorTheme === "dark"
      ? bodyClass.add(className)
      : bodyClass.remove(className);
  }, [colorTheme]);

  return [colorTheme, setColorTheme];
}

As you can see, this hook calls another hook called useLocalStorage , which allows the state to persist on refresh如您所见,此挂钩调用另一个名为useLocalStorage的挂钩,它允许 state 在刷新时保持不变

I got this hook from usehooks.com , and it's defined as:我从usehooks.com得到这个钩子,它被定义为:

import { useState } from "react";

const PREFIX = "my-app-";

export default function useLocalStorage(key: string, initialValue: string) {
  const prefixedKey = PREFIX + key;
  const [storedValue, setStoredValue] = useState(() => {
    if (typeof window === "undefined") {
      return initialValue;
    }

    try {
      const item = window.localStorage.getItem(prefixedKey);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.log(error);
      return initialValue;
    }
  });

  function setValue(value: unknown) {
    try {
      /**
       * Don't fully understand function typing here
       */
      const valueToStore =
        value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      if (typeof window !== "undefined") {
        window.localStorage.setItem(prefixedKey, JSON.stringify(valueToStore));
      }
    } catch (error) {
      console.log(error);
    }
  }

  return [storedValue, setValue];
}

The colorTheme value is successfully being stored in localStorage , and the hook works on the initial application load, but I am having issues in components that call the useColorTheme hook, like in this DrawerContainer example: colorTheme值已成功存储在localStorage中,并且挂钩在初始应用程序加载时起作用,但我在调用useColorTheme挂钩的组件中遇到问题,如这个DrawerContainer示例:

export default function DrawerContainer() {

  // calling the hook
  const [theme, setTheme] = useColorTheme();

  // component does not re-render when ThemeToggle component toggles theme state

  return (
    <div className="flex items-center justify-between gap-2">
      <FiMenu size={30} className="lg:hidden mr-4 cursor-pointer" />
      <Image
        className="ml-10 mr-10 sm:mr-2 sm:ml-0 cursor-pointer"
        src={
          theme === "light"
            ? "/images/fiverr-logo.png"
            : "/images/fiverr-logo-white.png"
        }
        alt="logo"
        width={100}
        height={100}
      />
      {!user && <AuthButtons showUnderSmall />}
      <ThemeToggle />
    </div>
  );
}

When the value of colorTheme is toggled in some other component in my application (such as my ThemeToggle component), the changed state is not being picked up in my DrawerContainer component, which prevents logic that reads from this state from happening.colorTheme的值在我的应用程序的某些其他组件(例如我的ThemeToggle组件)中切换时,更改的 state 不会在我的DrawerContainer组件中被拾取,这会阻止从这个 state 读取的逻辑发生。

I've verified that the state is indeed changing in my browser Dev Tools, so why is my DrawerContainer component not re-rendering?我已经验证 state 确实在我的浏览器开发工具中发生了变化,那么为什么我的DrawerContainer组件没有重新呈现?

Thank you very much in advance.非常感谢你提前。 Any insight is greatly appreciated.非常感谢任何见解。

Wen't thru your code and see the issue.我们没有通过您的代码看到问题。

Your custom hook is independent,that means in every component it has its own state.您的自定义挂钩是独立的,这意味着在每个组件中它都有自己的 state。

So for example you have two components A,B and both using hook.因此,例如,您有两个组件 A、B 并且都使用 hook。

when you changing theme using component A ,new state is being enclosed inside of component A and not being passed down to B or other components that using hook as well.当您使用组件A更改theme时,新的 state 被包含在组件A内,而不是传递给B或其他使用 hook 的组件。

To solve your issue you have to use Context API and use single state which will be passed down to other components using context.要解决您的问题,您必须使用上下文 API 并使用单个 state,它将使用上下文传递给其他组件。

Check out this https://reactjs.org/docs/context.html看看这个https://reactjs.org/docs/context.html

This could be implemented like this(pseudo code):这可以像这样实现(伪代码):

1.Creating context 1.创建上下文

const themeContext = React.createContext();

2.Implementing hook which will be reused in components to catch context state. 2.实现钩子,将在组件中重用以捕获上下文 state。

function useTheme() {
    return useContext(themeContext);
}

3.Implementing provider component which should be used at Root level so that can be utilized in all children components. 3.实现应该在根级别使用的提供者组件,以便可以在所有子组件中使用。

    const {Provider} = themeContext;

    function ThemeProvider(props) {
        const [theme,setTheme] = useState(() => //grabbing initial theme);
    
        const context = useMemo(() => ({theme, setTheme}), [theme])
        return <Provider value={context}>{props.children}</Provider>
    }

4.Wrapping components which will utilize your context. 4.包装将利用您的上下文的组件。

function App() {
    return (
        <ThemeProvider>
          <MyComponent />
        </ThemeProvider>
    )
}

Hope it helps!希望能帮助到你!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM