前言
紧接前文,前面说到只要我们通过修改antd 的 theme 算法就可以实现动态主题切换了,这把我们来实操下,那,进入小试牛刀环节,其实在进入小试牛刀环节之前还有一个准备环节,我知道你们都准备好了,所以我们直接开干,但是在浏览的过程中有一个问题需要你们思考下,就是为什么要这么写,可不可以那样写,那样写了会怎样,好了,开冲 ⛷。
小试牛刀
- 编写
useTheme.ts
import { useEffect, useState } from 'react';
const useTheme = (): [string, () => void] => {
const [theme, setTheme] = useState('light');
useEffect(() => {
const storeTheme = window.localStorage.getItem('theme');
if (storeTheme) setTheme(storeTheme);
}, []);
const toggleTheme = () => {
setTheme((prevTheme) => {
const value = prevTheme === 'light' ? 'dark' : 'light';
window.localStorage.setItem('theme', value);
return value;
});
};
return [theme, toggleTheme];
};
export default useTheme;
- 创建
ThemeContext.ts
import React, { useContext } from 'react';
export interface ThemeContextValue {
theme: string;
toggleTheme?: () => void;
}
export const ThemeContext = React.createContext<ThemeContextValue>({
theme: 'light',
});
export const useThemeContext = () => {
return useContext(ThemeContext);
};
export default BaseLayout;
- 创建
ThemeProvider.tsx
import React from 'react';
import { ThemeContext } from '@/components/Provider/ThemeContext';
import { ConfigProvider, theme } from 'antd';
import useTheme from '@/hooks/useTheme';
import locale from 'antd/locale/zh_CN';
import 'dayjs/locale/zh-cn';
export interface ThemeProviderProps {
children: React.ReactNode;
}
export function ThemeProvider(props: ThemeProviderProps) {
const [value, toggleTheme] = useTheme();
return (
<ThemeContext.Provider value={{ theme: value, toggleTheme }}>
<ConfigProvider
locale={locale}
theme={{
algorithm:
value === 'light' ? theme.defaultAlgorithm : theme.darkAlgorithm,
}}
>
{props.children}
</ConfigProvider>
</ThemeContext.Provider>
);
}
- 在
_app.tsx
中使用ThemeProvider
import React from 'react';
import '@/styles/globals.css';
import { AppProps } from 'next/app';
import { AuthProvider } from '@/components/Provider/AuthProvider';
import { ThemeProvider } from '@/components/Provider/ThemeProvider';
export default function App({
Component,
pageProps: { ...pageProps },
}: AppProps) {
return (
<>
<ThemeProvider>
<AuthProvider>
<Component {...pageProps} />
</AuthProvider>
</ThemeProvider>
</>
);
}
问答环节
Q1. 为什么要使用将 ConfigProvider
放在 _app.tsx
里,直接像下面这样在 BaseLayout 中使用 ConfigProvider 不好吗?
//...
const BaseLayout = ({ children }: any) => {
const [value, setValue] = useTheme();
return (
<ConfigProvider
locale={locale}
theme={{
algorithm:
value === 'light' ? theme.defaultAlgorithm : theme.darkAlgorithm,
}}
>
<Layout style={{ minHeight: '100vh' }}>
1111
</Layout>
</ConfigProvider>
)
}
使用 BaseLayout
const TableList: React.FC = () => {
return (
<BaseLayout>
1122
</BaseLayout>
)
}
答:因为在暗黑模式下,这样使用在切换页面时会有闪烁问题,就是每次切换页面都有一个短暂的由白变黑的页面闪烁问题,这个之前忘记录频了,自行脑补下哈。
但是当你把 ConfigProvider
放在 _app.tsx
下就不会有闪烁的问题了,知道为什么吧,当你把 ConfigProvider
放在 _app.tsx
下,这玩意它就是全局的,每次打开新页面都不用再去问当前是什么主题,所以不会闪烁,了解了吧。
Q2. 为什么要使用 react 的 Context 和 Provider
有人可能会问直接使用 useTheme
和 ConfigProvider
实现不行吗,为什么还要使用 react 的 Context
和 Provider
?其实核心就一句话,Context 和 Provider 这哥们俩实现了组件间数据共享。解释一下就是当你在其他组件中(例如在 Header 组件)直接用 useTheme
切换主题时,_app.tsx
中的 ConfigProvider
是感知不到主题变化的,而改变 Context
是全局都可以感知到的,所以,这就是使用 Context
和 Provider
的原因。
Q3. 那怎么切换主题啊
ok,示例:通过点击 button 来切换主题
const Header: React.FC<Props> = () => {
const { user } = useAuthContext();
const { theme, toggleTheme } = useThemeContext();
return (
<Layout.Header>
<Button
type='text'
onClick={() => toggleTheme && toggleTheme()}
icon={
theme === 'dark' ? (
<IconDark style={{ height: 20, width: 20 }} />
) : (
<IconLight style={{ height: 24, width: 24 }} />
)
}
/>
</Layout.Header>
);
};
export default Header;
Q4. 你还想到了其他的实现方式吗,欢迎在评论区留言。
最后
听说你想直接用这个模板项目(Next集成Antd 模板项目),安排,上链接:
感谢老铁的三连🤞