Использование wrapRootElement для предоставления контекста в GatsbyJS - PullRequest
1 голос
/ 26 апреля 2020

Я пытаюсь добавить переключатель темного режима в приложение Gatsby Js, используя следующий подход:

Используйте API-интерфейс Gatsby wrapRootElement, чтобы обернуть приложение в провайдер контекста:

// gatsby-browser.js

import ThemeProvider from '@components/themeProvider';
import Layout from '@components/layout';

export const wrapRootElement = data => <ThemeProvider {...data} />;
export const wrapPageElement = data => <Layout {...data} />;

Инициализация темы по умолчанию из localStorage или из настроек браузера пользователя:

// themeProvider.js

const ThemeContext = React.createContext({
  darkMode: false,
  toggleDarkMode: () => {},
});

const ThemeProvider = ({ element }) => {
  const [darkMode, setDarkMode] = useLocalStorage(
    'darkMode',
    window.matchMedia('(prefers-color-scheme: dark)').matches
  );

  const toggleDarkMode = () => {
    setDarkMode(!darkMode);
  };

  return (
    <ThemeContext.Provider value={{ darkMode, toggleDarkMode }}>
      {element}
    </ThemeContext.Provider>
  );
};

и, наконец, компонент layout: (обратите внимание на журнал консоли)

// layout.js

const Layout = ({ element }) => (
  <ThemeContext.Consumer>
    {({ darkMode }) => {
      console.log({ darkMode });
      return (
        <App theme={darkMode ? themes.dark : themes.light}>
          {element}
        </App>
      );
    }}
  </ThemeContext.Consumer>
);

Это работает отдельно от одной проблемы. При первоначальной загрузке с сохраненным состоянием true (т. Е. DarkMode включен) приложение отображает светлую тему. Журнал консоли, приведенный выше, отображает правильное значение true, но приложение не отображает правильную тему.

Если я затем дважды переключаюсь, устанавливая значение темы с true -> false -> true, тогда darkMode отображается правильно - поэтому logi c работает должным образом, за исключением случаев, когда страница загружается впервые.

Я полагаю, это как-то связано с серверной частью Gatsby, отрисовывающей приложение с состоянием по умолчанию false, установленным в вызове createContext в themeProvider.js но я не уверен, как решить эту проблему.

Ответы [ 2 ]

1 голос
/ 26 апреля 2020

Когда пользователь открывает ваш сайт Gatsby, вам нужно активировать функциональность вашей темы. Так как локальное хранилище пусто при первом посещении, вы можете переключать только ту тему, которую хотите переключить дважды, потому что только там определен режим темы.

Вам необходимо определить, хочет ли пользователь светлый или темный при первом посещении сайта.

Добавьте функцию в свой класс провайдера тем:

const determineThemeMode = () => {
  // depending on user settings, set the right theme mode
}

Запустите этот новый работает либо на записи клиента , визуализации клиента , либо onInitialClientRender . Вам, вероятно, нужно запустить его только в одной функции. Попробуйте сначала все, а затем устраните.


В качестве альтернативы есть плагины, такие как gatsby-plugin-dark-mode .

0 голосов
/ 03 мая 2020

Это рабочее решение, которое я придумал. Два ключевых элемента:

1) проверка блока рендеринга в поставщике контекста {typeof darkMode === 'boolean' && element}, который предотвращает рендеринг компонентов страницы без темы, что может привести к ошибкам в стилевых компонентах

2) useEffect ловушка в ловушке, которая предотвращает загрузку значения темы до тех пор, пока компонент не отобразит клиентскую сторону и не станет доступен localStorage.

// ThemeContext

import useTheme from '@hooks/useTheme';

const ThemeContext = React.createContext({
  darkMode: null,
  toggleDarkMode: () => {},
});

const ThemeWrapper = ({ element }) => {
  const [darkMode, setDarkMode] = useTheme(null);

  const toggleDarkMode = () => {
    setDarkMode(!darkMode);
  };

  return (
    <ThemeContext.Provider value={{ darkMode, toggleDarkMode }}>
      {typeof darkMode === 'boolean' && element}
    </ThemeContext.Provider>
  );
};

export default ThemeContext;

export { ThemeWrapper };

// useTheme hook

export default initialValue => {
  const [storedValue, setStoredValue] = useState(initialValue);

  useEffect(() => {
    const storedState = getItem(storageId);
    const query = window.matchMedia('(prefers-color-scheme: dark)');

    if (typeof storedState === 'boolean') {
      setStoredValue(storedState);
    } else {
      setStoredValue(query.matches);
    }
  }, []);

  const setValue = value => {
    setStoredValue(value);
    setItem(storageId, value);
  };

  return [storedValue, setValue];
};


Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...