Next+Antd 实现 Theme 切换

前言

紧接前文,前面说到只要我们通过修改antd 的 theme 算法就可以实现动态主题切换了,这把我们来实操下,那,进入小试牛刀环节,其实在进入小试牛刀环节之前还有一个准备环节,我知道你们都准备好了,所以我们直接开干,但是在浏览的过程中有一个问题需要你们思考下,就是为什么要这么写,可不可以那样写,那样写了会怎样,好了,开冲 ⛷。

小试牛刀

  1. 编写 useTheme.ts
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;
  1. 创建 ThemeContext.ts
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;
  1. 创建 ThemeProvider.tsx
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>  
    );  
}
  1. 在 _app.tsx 中使用 ThemeProvider
tsx 复制代码
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 不好吗?

tsx 复制代码
//...
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

tsx 复制代码
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 来切换主题

tsx 复制代码
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 模板项目),安排,上链接:

github.com/lijunping36...

感谢老铁的三连🤞

相关推荐
itmigrate1 天前
前端项目里,Object.is 并不是 === 的平替
前端
冬奇Lab1 天前
一天一个开源项目(第248篇):CleanYourTV —— 让 AI 编程助手通过 Wi-Fi 给安卓电视去广告、换上 Apple TV 风格主屏
开源·资讯
wflynn1 天前
GitHub 今日推荐|gpu-unlock-ada:Ada 显卡张量算力翻倍解锁
开源·github·c
小大宇1 天前
Vue知识点6-computed计算属性
前端·javascript·vue.js
程序员海军1 天前
尤雨溪 : Vue 4 已经在规划中了!
前端·javascript·vue.js
2601_949970691 天前
HumanizeDuration 跨端三方库仓颉适配全流程
前端·javascript·flutter
SL_staff1 天前
JVS私有化文档系统上线避坑指南:5大运维与业务协同断点及检测加固实践
java·开源·全栈
风骏时光牛马1 天前
后端服务接口开发与性能优化
前端
kyriewen1 天前
我让 4 个 AI 互相 review 代码,最温柔的那个下手最狠
前端·程序员·ai编程
计算机魔术师1 天前
493家公司白砸AI钱?Apollo这组数据戳破了幻觉
前端