thumbnail

Themeの使い方~TypeScriptでstyled-components〜

この記事では、React+TypeScriptでstyled-componentsのThemeを利用する方法について解説します。Themeに型付けする方法等の説明していますが、型情報を無視すればJavaSciripterでも読めます。

Themeとは

Themeを使うと、styled-componentsで利用する値を『共通の値』として色々なパーツで使いまわせます。themeで値を一元管理することで、デザインの一貫性を担保できたり修正に強いコンポーネントの作成が可能となります。

なお、Theme以外のstyled-componentsの解説は次の記事で行っております。

最も基本的な使い方

まずは、テーマ(theme)をオブジェクトで記述します。
ここでは例として”main”と”sub”という値をthemeとして定義しています。

const theme = {
  main: "blue",
  sub: "purple"
}

このようにthemeとして定義したmainとsubは、styled-componentsのpropsから自由にアクセスできるようになります。

const Button1 = styled.button`
  background-color: ${props => props.theme.main};
  padding: 10px;
`

const Button2 = styled.button`
  background-color: ${props => props.theme.sub};
  padding: 10px;
`

ただし、themeを使いたいコンポーネントはThemeProviderで囲む必要があります。この時、定義したthemeをpropsとして渡します。

<ThemeProvider theme={theme}>
  <Button1>Button1</Button1>
  <Button2>Button2</Button2>
</ThemeProvider>

続いて発展的な使い方を見ていきます。

Themeの型について

宣言のマージを利用して、Themeの型を型定義ファイル(d.ts)で宣言することができます。例えば、styled.d.tsを用意し、先ほどのThemeの型を宣言します。

import 'styled-components'
declare module 'styled-components' {
  export interface DefaultTheme {
    main: string
    sub: string
  }
}

DefaultThemeはもともと用意されている型ですが中身は空で、このようにThemeの型として拡張して利用することが可能です。あとはThemeの型が必要なところで、次のようにimportして使えます。

import type { DefaultTheme } from 'styled-components'

関数テーマ~部分的にThemeを変える~

『親ThemeProvider』の中に『子ThemeProvider』を定義し、子ThemeProviderの中だけでThemeの値を変更することが可能です。その際に利用するのが関数テーマ(function theme)です。

例えば、次の例のように、mainとsubの値を逆にする関数を考えます。

const theme = {
  main: 'blue',
  sub: 'purple',
}

const invertTheme = ({ main, sub }: DefaultTheme) => ({
  main: sub,
  sub: main,
})

親ThemeProviderにtheme, 子ThemeProviderにinvertThemeを与えると、子Themeの中だけでmainとsubの値が逆になります。

<ThemeProvider theme={theme}>
  <Button1>Button1</Button1>
  <Button2>Button2</Button2>
  <Space />
  <ThemeProvider theme={invertTheme}>
    <Button1>Invert1</Button1>
    <Button2>Invert2</Button2>
  </ThemeProvider>
</ThemeProvider>

styled-components外でThemeを使う

『styled.タグ』で定義したstyled-componentsではなく、通常のコンポーネントでThemeの値を利用する方法を3つ紹介します。

withThemeでラップする高次のコンポーネント

コンポーネントをwithThemeでラップすると、themeにアクセスが可能となります。

const Component = withTheme(({ theme }: { theme: DefaultTheme }) => {
  return <>{console.log(theme.main)}</> //blueと表示される
})
const Wrapper = () => {
  return (
    <ThemeProvider theme={theme}>
      <Component />
    </ThemeProvider>
  )
}

useContextを使う

useContext(ThemeContext)でThemeの値にアクセスできるようになります。

import { useContext } from 'react'
import { ThemeContext } from 'styled-components'
const Component = () => {
  const themeContext = useContext(ThemeContext)
  return <>{console.log(themeContext.main)}</> //blue
}
const Wrapper = () => {
  return (
    <ThemeProvider theme={theme}>
      <Component />
    </ThemeProvider>
  )
}

useThemeを使う

もっとも簡単なのが、usetThemeカスタムフックを利用する方法です。

import { useTheme } from 'styled-components'
const Component = () => {
  const theme = useTheme()
  return <>{console.log(theme.main)}</> //blue
}
const Wrapper = () => {
  return (
    <ThemeProvider theme={theme}>
      <Component />
    </ThemeProvider>
  )
}

theme propsで値のオーバーライド

styled-componentsのpropsにDefaultTheme型のthemeプロパティを渡すと、値がオーバーライドされます。

const theme = {
  main: 'blue',
  sub: 'purple',
}
const Button = styled.button`
  background-color: ${(props) => props.theme.main};
  border-color: ${(props) => props.theme.sub};
`
const Wrapper = () => {
  return (
    <ThemeProvider theme={theme}>
      <Button theme={{ main: "tomato", sub: "maroon" }}>Button</Button>
    </ThemeProvider>
  )
}

まとめ

以上、styled-componentsのThemeに関連する解説をReact + TypeScriptで行いました。
その他のstyled-componentsの解説は次の記事で行っております。