useContext 範例:CustomTheme

使用 useContext 實作 useCustomTheme 紀錄。react.v18, material UI.v5, CustomThemeProvider

引言

使用 useContext 實作 useCustomTheme 紀錄。

源碼紀錄

hooks/useCustomTheme
/*
 * To customize the theme: Dark mode with a custom palette
 * 參考:[Dark mode with a custom palette](https://mui.com/material-ui/customization/dark-mode/#dark-mode-with-a-custom-palette)
 * 參考:[Toggling color mode](https://mui.com/material-ui/customization/dark-mode/#toggling-color-mode)
 */
import type { ReactChild } from 'react'
import React, { useContext, useReducer } from 'react'
import { createTheme, ThemeProvider } from '@mui/material/styles'
import { zhTW } from '@mui/material/locale'
import { IconButton } from '@mui/material'
// CSS
import DarkIcon from '@mui/icons-material/Brightness4'
import WhiteIcon from '@mui/icons-material/Brightness7'

//-----------------------------------------------------------------------------
//## Resource
const whiteTheme = createTheme(
  {
    palette: {
      primary: { main: '#BF4690' },
    },
  },
  zhTW, // Locale text:Use the theme to configure the locale text globally.
);

const darkTheme = createTheme(
  {
    palette: {
      mode: 'dark',
    },
  },
  zhTW, // Locale text:Use the theme to configure the locale text globally.
);

//-----------------------------------------------------------------------------
//## useContext
//# first, create empty context, 建構時給與初始值以避免當掉。
const CustomThemeContext = React.createContext({
  colorMode: 'white',
  toggleColorMode: () => { }
});

export function useCustomTheme() {
  return useContext(CustomThemeContext)
}

export const CustomThemeProvider = (props: { children: ReactChild }) => {
  //# 真正共享的實體在此建立
  const [colorMode, toggleColorMode] = useReducer((mode) => mode === 'white' ? 'dark' : 'white', 'white')

  return (
    <CustomThemeContext.Provider value={{ colorMode, toggleColorMode }}>
      <ThemeProvider theme={colorMode === 'dark' ? darkTheme : whiteTheme}>
        {props.children}
      </ThemeProvider>
    </CustomThemeContext.Provider>)
}

//=============================================================================
//## helper component
//# 用來切換 while/dark theme
export const ToggleBrightnessButton = () => {
  const themeMode = useCustomTheme()
  return (
    <IconButton onClick={themeMode.toggleColorMode} color="inherit" >
      {themeMode.colorMode === 'dark' ? <DarkIcon /> : <WhiteIcon />}
    </IconButton>
  )
}

應用:掛入 CustomThemeProvider

index.tsx
import React from 'react'
import { createRoot } from 'react-dom/client'
import { Provider } from 'react-redux'
import { BrowserRouter } from 'react-router-dom'
import store from './store/store'
import App from './App'
import reportWebVitals from './reportWebVitals'
import * as serviceWorkerRegistration from './serviceWorkerRegistration'
import { CustomThemeProvider } from 'hooks/useCustomTheme'
import { CacheProvider } from '@emotion/react'
import createCache from '@emotion/cache'

const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href') as string

const root = createRoot(
  document.getElementById('root') as HTMLElement
);

export const muiCache = createCache({
  key: 'mui',
  prepend: true,
});

root.render(
  <React.StrictMode>
    <Provider store={store}>
      <CacheProvider value={muiCache}>
        <BrowserRouter basename={baseUrl}>
          <CustomThemeProvider> /* 掛入 CustomThemeProvider */
            <App />
          </CustomThemeProvider>
        </BrowserRouter>
      </CacheProvider>
    </Provider>
  </React.StrictMode>
);

serviceWorkerRegistration.unregister()
reportWebVitals()

應用:切換 while / dark theme

views/Banner.tsx
import React from 'react'
import { AppBar, Toolbar, Typography } from '@mui/material'
// hooks
import { ToggleBrightnessButton } from 'hooks/useCustomTheme'
// CSS icons
import AdbIcon from '@mui/icons-material/Adb'

export default function Banner() {
  return (
    <AppBar position="static">
      <Toolbar>
        <AdbIcon />
        <Typography variant="h6">
          My First React.v18 App
        </Typography>
        <ToggleBrightnessButton />
      </Toolbar>
    </AppBar>
  )
}

EOF

Last updated