當(dāng)前主題默認(rèn)為 “dard” 暗黑模式湖笨。這次將支持“暗黑模式”與“明亮模式”之間的切換袍患。
開(kāi)發(fā)
創(chuàng)建一個(gè)“主題”上下文文件
explorer/src/components/change-theme/change-theme-context.tsx
'use client'
import createCtx from '@/lib/create-ctx'
import React from 'react'
export const ChangeThemeContext = createCtx<string>('')
export const ChangeThemeProvider: React.FC<React.ProviderProps<string>> = ({ value, children }) => {
return <ChangeThemeContext.ContextProvider value={value}>{children}</ChangeThemeContext.ContextProvider>
}
再創(chuàng)建一個(gè)內(nèi)聯(lián) cookie 的文件
explorer/src/components/change-theme/inject-cookie.tsx
import { ChangeThemeProvider } from '@/components/change-theme/change-theme-context'
import React from 'react'
import { cookies } from 'next/headers'
const InjectCookieChangeThemeProvider: React.FC<React.PropsWithChildren> = ({ children }) => {
const theme = cookies().get('theme')?.value || 'dark'
return <ChangeThemeProvider value={theme}>{children}</ChangeThemeProvider>
}
export default InjectCookieChangeThemeProvider
為 SSR 渲染時(shí)獲取 cookie 內(nèi)的 theme 的值慕蔚,保持服務(wù)端與客戶端渲染一致。避免主題切換閃屏。
該主題的改變會(huì)影響所有的組件渲染贞言。所以將 InjectCookieChangeThemeProvider 內(nèi)聯(lián) cookie 的組件插入最頂層的 layout 組件內(nèi)吃警。
explorer/src/app/layout.tsx
...
import InjectCookieChangeThemeProvider from '@/components/change-theme/inject-cookie'
...
const RootLayout: React.FC<React.PropsWithChildren> = ({ children }) => (
<html lang="en">
<body className={inter.className}>
<InjectCookieChangeThemeProvider>
...
</InjectCookieChangeThemeProvider>
</body>
</html>
)
export default RootLayout
antd 的 ConfigProvider 上下文通過(guò) ChangeThemeContext 上下文獲取當(dāng)前主題糕篇。
explorer/src/lib/antd-registry.tsx
'use client'
...
import { ChangeThemeContext } from '@/components/change-theme/change-theme-context'
...
const AntdConfigProvider: React.FC<React.PropsWithChildren> = ({ children }) => {
const select_theme = ChangeThemeContext.useStore()
return (
<ConfigProvider
componentSize="small"
theme={{
algorithm: select_theme === 'dark' ? theme.darkAlgorithm : theme.defaultAlgorithm,
}}
>
<Layout style={{ height: '100vh' }}>{children}</Layout>
</ConfigProvider>
)
}
...
創(chuàng)建一個(gè)改變主題的dropdown 下拉菜單,并插入頁(yè)面右上角位置酌心。
'use client'
import React from 'react'
import { Button, Dropdown } from 'antd'
import { DarkIcon, LightIcon } from '@/components/icon/theme'
import { ChangeThemeContext } from '@/components/change-theme/change-theme-context'
import { useCookieState } from 'ahooks'
const ChangeThemeDropdown: React.FC = () => {
const theme = ChangeThemeContext.useStore()
const changeThemeDispatch = ChangeThemeContext.useDispatch()
const [, changeCookies] = useCookieState('theme')
return (
<Dropdown
menu={{
selectedKeys: [theme],
onClick: (item) => {
changeThemeDispatch(item.key)
changeCookies(item.key)
},
items: [
{ key: 'light', icon: <LightIcon />, label: '明亮模式' },
{ key: 'dark', icon: <DarkIcon />, label: '暗黑模式' },
],
}}
>
<Button type="text" icon={theme === 'dark' ? <DarkIcon /> : <LightIcon />} />
</Dropdown>
)
}
export default ChangeThemeDropdown
當(dāng)點(diǎn)擊下拉菜單項(xiàng)時(shí)改變 ChangeThemeContext 上下文的值拌消,并將當(dāng)前改變值寫(xiě)入 cookie 內(nèi)。
效果
到此安券,完成了改變主題的功能墩崩,通過(guò)將主題狀態(tài)寫(xiě)入 cookie 內(nèi),保證服務(wù)端與客戶端的渲染一致侯勉。