Реагировать на изменение состояния пользовательского хука, не вызывая повторного рендеринга

Я создал собственный реагирующий хук, который позволяет пользователям переключать цветовую тему моего приложения (т. е. «светлая» или «темная»), и изменение состояния не приводит к повторному рендерингу компонентов, вызывающих хук.

Крючок называется 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];
}

Как видите, этот хук вызывает другой хук под названием useLocalStorage, который позволяет сохранять состояние при обновлении.

Я получил этот хук от 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.info(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.info(error);
    }
  }

  return [storedValue, setValue];
}

Значение 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>
  );
}

Когда значение colorTheme переключается в каком-либо другом компоненте моего приложения (например, в моем компоненте ThemeToggle), измененное состояние не фиксируется в моем компоненте DrawerContainer, что предотвращает выполнение логики, считывающей это состояние.

Я убедился, что состояние действительно меняется в моем браузере Dev Tools, так почему мой компонент DrawerContainer не перерисовывается?

Заранее большое спасибо. Любое понимание очень ценится.

У вас есть что-то вроде CodeSandbox, чтобы более эффективно разобраться в проблеме?

Bikas Lin 24.11.2022 02:35
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
1
70
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Не просматривайте ваш код и не смотрите на проблему.

Ваш пользовательский хук независим, это означает, что в каждом компоненте он имеет свое собственное состояние.

Так, например, у вас есть два компонента A, B, и оба используют хук.

когда вы меняете theme с помощью компонента A, новое состояние заключается внутри компонента A и не передается B или другим компонентам, которые также используют хук.

Чтобы решить вашу проблему, вы должны использовать Context API и использовать одно состояние, которое будет передаваться другим компонентам с использованием контекста.

Посмотрите это https://reactjs.org/docs/context.html

Это может быть реализовано так (псевдокод):

1.Создание контекста

const themeContext = React.createContext();

2. Реализация хука, который будет повторно использоваться в компонентах для перехвата состояния контекста.

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

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. Компоненты упаковки, которые будут использовать ваш контекст.

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

Надеюсь, поможет!

Это имеет большой смысл, спасибо!

Shadee Merhi 06.12.2022 06:25

Другие вопросы по теме