jpnykw’s blog

軽率なアウトプットをします

styled-componentsでラップした複数の子要素に同じpropsを同時に渡す

タイトルの通りです。あるコンポーネント内で配下にある全ての子要素(styled-componentsでラップされたものであるとします)に共通したpropsを渡す場合のやり方の話です。愚直に書いていたらキリがなく最悪なコードが出来上がります。

結論

styled-components/ThemeContext を使おう。

import { ThemeContext } from 'styled-components'

具体例

まずこんな感じの複雑なコンポーネント Component が存在しているとします。多くのコンポーネントやDOMを内包していますが、それらは全てstyled-componentsでラップされています。

const A = styled.div`
  /* ... */
  ${props => props.value}
  /* ... */
`

const B = styled.div`
  /* ... */
  ${props => props.value}
  /* ... */
`

const C = styled.div`
  /* ... */
  ${props => props.value}
  /* ... */
`

// ...

const Z = styled.div`
  /* ... */
  ${props => props.value}
  /* ... */
`

const Component = () => {
  return (
    <Container>
      <A />
      <B />
      <C />
      {/* ... */}
      <Z />
    </Container>
  )
}

ここで、全ての(あるいは殆どの)内包したコンポーネントvalue という共通した値を使いまわしたいとします。(この valueuseContext などで更に上層から受け取ったものとします)

そんな時に、こんなこと↓なんてやていられません。

const Component = () => {
  return (
    <Container>
      <A value={value} />
      <B value={value} />
      <C value={value} />
      {/* ... */}
      <Z value={value} />
    </Container>
  )
}

こういうときは ThemeContext を使いましょう、一発で解決します。

const Component = () => {
  return (
    <ThemeContext.Provider value={/* 任意の値 */}>
      <Container>
        <A />
        <B />
        <C />
        {/* ... */}
        <Z />
      </Container>
    </ThemeContext.Provider>
  )
}

ただし、注意点として呼び出す際のpropsのプロパティがthemeになるので、既に違うプロパティで定義してる場合は修正しましょう。

- ${props => props.value}
+ ${props => props.theme}

また当然ではありますがthemeにオブジェクトを渡すことでpropsを一層ラップした状態でこれまで通りに値を扱えます。

interface Attrs {
  // 渡したいpropsの型定義
}

const A = styled.div`
  /* ... */
  ${({ theme: props }: { theme: Attrs }) => props.aaa}
  ${({ theme: props }: { theme: Attrs }) => props.bbb}
  ${({ theme: props }: { theme: Attrs }) => props.ccc}
  /* ... */
`

const Component = () => {
  const attrs: Attrs = { /* ... */ }

  return (
    <ThemeContext.Provider value={attrs}>
      <Container>
        <A />
        <B />
        <C />
        ...
        <Z />
      </Container>
    </ThemeContext.Provider>
  )
}

以上です。