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

有人可能会问直接使用 useThemeConfigProvider 实现不行吗,为什么还要使用 react 的 ContextProvider?其实核心就一句话,Context 和 Provider 这哥们俩实现了组件间数据共享。解释一下就是当你在其他组件中(例如在 Header 组件)直接用 useTheme 切换主题时,_app.tsx 中的 ConfigProvider 是感知不到主题变化的,而改变 Context 是全局都可以感知到的,所以,这就是使用 ContextProvider 的原因。

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...

感谢老铁的三连🤞

相关推荐
大模型码小白24 分钟前
数据可视化:AI 生成 HTML5 动态交互式数据图表
前端·数据库·人工智能·深度学习·机器学习·信息可视化·html5
八荒启·交互动画25 分钟前
Web特效01—什么是渲染
前端·javascript·网页特效·八荒启-交互动画
小肥君29 分钟前
前端测试websocket
前端·websocket·状态模式
南京兴帝文化传媒有限公司1 小时前
基于地图平台的本地商户信息优化:药店夜间服务标注与客户转化实操
前端·javascript·数据库·人工智能·geo 优化·geo优化避坑·ai搜索获客
八荒启·交互动画1 小时前
Web特效04——GPUvs CPU,为什么图形计算要交给GPU,什么是“并行计算”
前端
葫三生1 小时前
三生原理与《涌现:从简单规则到复杂世界》在“简单规则生成复杂系统”核心思路上存在理论呼应?
人工智能·科技·算法·机器学习·开源
呃呃呃呃ex1 小时前
2026年古法编程的末法时代,如何评估自己完成迅速转行
前端·后端
南京兴帝文化传媒有限公司1 小时前
地图SEO与AI搜索优化结合实践:宁国摄影工作室本地获客案例分析
大数据·前端·人工智能·geo 优化·geo优化避坑
今朝唯我少年郎1 小时前
Codex实战用AI 写运维脚本
开源
用户921080262861 小时前
前端 Vue 专栏 07:模板编译、虚拟 DOM、Diff 与 key
前端