React useContextとemotion-themingを使ってテーマ切り替え機能を実装する🌔
emotion-themingを利用して、テーマ切り替え機能を実装します。Twitterのと一緒です。ボタン押して、toggleできるものを目指します。
Install
とりあえず
npx create-react-app dark-mode --typescript
emotion
yarn add @emotion/core @emotion/styled emotion-theming
モード切り替え
React.Contextに今どのモードなのかを保存、useContextを使って、コンポーネントからモード切り替えをします。
まずはContextを用意
// themeContext.ts import { createContext, useContext } from 'react'; interface ThemeContextType { colorMode: ColorMode; setColorMode: () => void; } const defaultContext: ThemeContextType = { colorMode: 'light', // 現在のモードを管理 setColorMode: () => {}, // colorMode書き換え用の関数を渡す }; export const ThemeContext = createContext<ThemeContextType>(defaultContext); export const useTheme = () => useContext(ThemeContext);
setColorMode
にcolorMode
を書き換えるための関数を渡します。(Providerのvalue propsで)
Theme用意
この辺はなんでもいいです。
import { ColorMode, Theme } from './types'; const lightTheme: Theme = { background: '#ffffff', color: '#000000', }; const darkTheme: Theme = { background: '#222639', color: '#f0f5fa', }; export function getTheme(colorMode: ColorMode): Theme { // mode受けてテーマ返す switch (colorMode) { case 'light': return lightTheme; case 'dark': return darkTheme; default: return lightTheme; } }
Providerの用意
上で定義した、getTheme()を使って、テーマを切り替えます
// ThemeProvider.tsx import React, { useState } from 'react'; import { ThemeProvider as EmotionProvider } from 'emotion-theming'; import { ThemeContext } from '../themeContext'; import { getTheme } from '../theme'; type ColorMode = 'light' | 'dark'; const ThemeProvider: React.FC = ({ children }) => { const [colorMode, setColorMode] = useState<ColorMode>('light'); function toggleColorMode() { // colorMode切り替え用関数 setColorMode(colorMode === 'light' ? 'dark' : 'light'); } return ( <EmotionProvider theme={getTheme(colorMode)}> <ThemeContext.Provider value={{ colorMode, setColorMode: toggleColorMode, }} > {children} </ThemeContext.Provider> </EmotionProvider> ); }; export default ThemeProvider;
あとは、コンポーネントから、useTheme() を使ってテーマを切り替えるだけ!
// App.tsx import React from 'react'; import './App.css'; import { useTheme } from './themeContext'; import styled from './components/styled'; const App: React.FC = () => { const { colorMode, setColorMode } = useTheme(); return ( <Container> <p>current color mode: {colorMode}</p> <button onClick={setColorMode}>toggle color mode</button> </Container> ); }; export default App; const Container = styled.div` height: 100%; background: ${props => props.theme.background}; color: ${props => props.theme.color}; `;
Typescriptを使っていると、emotionからimportしたstyled
を使うとtheme内にProviderで渡したプロパティの型情報が含まれていないため、使えないです。
styledに型情報をもたせて、それを替わりに使います。
参考:https://emotion.sh/docs/typescript#define-a-theme
// styled.tsx import styled, { CreateStyled } from '@emotion/styled'; import { Theme } from '../types'; export default styled as CreateStyled<Theme>; // 今回のthemeの型
やったね!
やったね。
おわり
以上です。Githubにあげてあります。