由于系统颜色模式,Chakra UI 页面刷新时颜色模式发生变化

Chakra UI color mode changes on page refresh, because of system color mode

我是 运行 带有 Chakra UI 的 NextJS,问题是当 useSystemColorMode 设置为 true 时,它会忽略用户在页面刷新时的选择并设置它回到系统颜色模式。对于前。系统颜色模式为深色,用户将按钮切换为浅色模式,然后刷新页面,这会自动撤消浅色模式并将其设置回深色。我希望它保持轻盈。

Note: When using system as initial color mode, the theme will change with the system preference. However, if another theme is manually selected by the user then that theme will be used on the next page load. To reset it to system preference, simply remove the chakra-ui-color-mode entry from localStorage.

我期望初始颜色模式是系统颜色模式(在我的例子中是深色),当用户将按钮切换到浅色模式时,在页面刷新时它应该保持浅色。

pages\dogs.tsx(这里是切换light/dark模式)

import { Button, useColorMode } from '@chakra-ui/react';
import Head from 'next/head';
import Link from 'next/link';

import { LoginWithCentredForm } from '@components/LoginWithCentredForm';

const Dogs = () => {
  const { colorMode, toggleColorMode } = useColorMode();

  return (
    <>
      <Head>
        <title>Dogs</title>
      </Head>
      <h1>Dogs</h1>
      <Button onClick={toggleColorMode}>
        Toggle {colorMode === 'light' ? 'Dark' : 'Light'}
      </Button>
      <h2>
        <Link href="/">
          <a>Go back home!</a>
        </Link>
        <LoginWithCentredForm />
      </h2>
    </>
  );
};

export default Dogs;

theme\index.tsx

import { extendTheme, ThemeConfig } from '@chakra-ui/react';

export const config: ThemeConfig = {
  initialColorMode: 'light',
  useSystemColorMode: true,
};

export const theme = extendTheme({
  config,
  fonts: {
    heading: 'Work Sans, system-ui, sans-serif',
    body: 'Inter, system-ui, sans-serif',
  },
});

pages\_documents.tsx

import { ColorModeScript } from '@chakra-ui/react';
import Document, { Html, Head, Main, NextScript } from 'next/document';

import { theme } from '../theme';

class MyDocument extends Document {
  render() {
    return (
      <Html>
        <Head>
          <link
            href="https://fonts.googleapis.com/css2?family=Work+Sans:wght@700&family=Inter:wght@400;500;600;700&display=swap"
            rel="stylesheet"
          />
        </Head>
        <body>
          <ColorModeScript initialColorMode={theme.config.initialColorMode} />
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

export default MyDocument;

pages\_app.tsx

import type { AppProps } from 'next/app';
import { ChakraProvider, CSSReset } from '@chakra-ui/react';

import { theme } from '../theme';

const App = ({ Component, pageProps }: AppProps) => {
  return (
    <ChakraProvider theme={theme}>
      <CSSReset />
      <Component {...pageProps} />
    </ChakraProvider>
  );
};

export default App;

我在组件安装后立即使用 useEffect 将颜色模式切换为正确的模式,延迟 1.5 秒,我注意到它在没有一些延迟的情况下无法工作。

useEffect(() => {
    if (localStorage.getItem('chakra-ui-color-mode') === 'light' && colorMode === 'dark') {
      setTimeout(() => toggleColorMode(), 1500)
    } else if (localStorage.getItem('chakra-ui-color-mode') === 'dark' && colorMode === 'light') {
      setTimeout(() => toggleColorMode(), 1500)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

基于the documentation,ChakraUI 不会自动将颜色模式与 LocalStorage 同步。你需要把 <ColorModeScript /> 放在第一位,所以它会与 LocalStorage 同步。

对于基本的 React 应用程序,执行此操作:

import { ColorModeScript } from '@chakra-ui/react'
// change to 'react-dom' if you are using React < 18
// here is React 18 example
import ReactDOM from 'react-dom/client'
import App from './App'
import theme from './theme'


// change to proper ReactDOM.render if you are using React < 18
// here is React 18 example
ReactDOM.createRoot(document.getElementById('root')!).render(
  <>
    {/* here is the magic */}
    <ColorModeScript initialColorMode={theme.config.initialColorMode} />

    <ChakraProvider theme={theme}>
      <App />
    </ChakraProvider>
  </>,
);

但不要忘记将 useSystemColorMode 更改为 false,因为此行为将用系统颜色覆盖任何颜色,然后如果系统不可用则回退到 LocalStorage(用户偏好主题)它被配置为 true。然后,将 initialColorMode 设置为 'system',这样如果用户还没有切换颜色模式,ChakraUI 将回退到系统。

export const theme = extendTheme({
  config: {
    initialColorMode: 'system',
    useSystemColorMode: false,
  },
});

但在使用 StrictMode 时它对我不起作用。

对于 NextJS,您可以查看 the documentation

对于 React ***

我找到的最简单的解决方案是默认将脉轮存储 color-mode 在本地存储中,因此您可以使用 localStorage.getItem('chakra-ui-color-mode') 访问它 所以创建 theme.js 文件

import { extendTheme } from "@chakra-ui/react";
const theme = extendTheme({
  config: {
    initialColorMode: localStorage.getItem('chakra-ui-color-mode')||'dark',
  },
});
export default theme;

这将尝试从本地存储中获取,如果它没有从本地存储中获取值,它会将主题设置为深色,然后在触发切换时创建本地存储,因此创建 index.js 和app.js

index.js

import { ChakraProvider, CSSReset } from "@chakra-ui/react";
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
    root.render(
      <React.StrictMode>
        <ChakraProvider theme={theme}>
          <CSSReset />
            <App />
        </ChakraProvider>
      </React.StrictMode>
    ); 

app.js

import {
  Button,
  useColorMode,
} from "@chakra-ui/react";

const App = () => {
  const { colorMode, toggleColorMode } = useColorMode();
  return (
  <>
    <Button onClick={()=>toggleColorMode()}>Toggle</Button>
  </>
  );
};

export default App;

当您单击切换时,您将在本地存储值中看到一个键 ('chakra-ui-color-mode')

** 注意:这也将跨页保留。**