LoginSignup
31
13

More than 3 years have passed since last update.

Reactのchildrenにpropsを追加する方法

Last updated at Posted at 2020-06-08

TL;DR

React.cloneElementを使ってchildrenを複製することで任意のpropsを追加できます。

const newChildren = React.cloneElement(children, additionalProps)
// React.cloneElementの第一引数にchildren, 第二引数に追加したいpropsを渡す

React.cloneElementとは

React.cloneElement()はElementを複製するメソッドです。
引数に以下のような3つの値を受け取ります。

React.cloneElement(element, props, [...children])
// element: ReactのElement
// props: 複製するElementにわたすprops, elementがもともと持っていたpropsとマージされる
// children: 複製するElementにわたすchildren

childrenにpropsを追加する

React.cloneElement()を使ってchildrenを複製しましょう。
このときReact.cloneElement()の第二引数に追加したいpropsを渡すことで、childrenにpropsを追加することができます。

const Example = ({children}) => {
  const additionalProps = {hoge: 'hoge', ...}

  const newChildren = React.cloneElement(children, additionalProps)

  return (
    <div>{newChildren}</div>
  )
}

一点注意すべきこととして、React.cloneElementの第一引数にはElementしか取れないことがあります。
childrenはElement以外にstringやElementの配列の場合もあります。
そのため上記のExampleのchildrenにElement以外の値を渡した場合、エラーになります。

TypeScriptを使っている場合は以下のようにchildrenに型をつけるとよいでしょう。

interface Props {
  children: React.ReactElement;
}

const Example: React.FC<Props> = ({children}) => ( ... )

利用例

image.png

上記のようなchildrenに2つのElementをを受け取って、2カラムで表示するレイアウトコンポーネントを考えます。

childrenに2つのElementを受け取って、それぞれに対してstyleをpropsから渡せばよいです。

interface Props {
  children: [React.ReactElement, React.ReactElement];
}

const TwoColumnLayout: React.FC<Props> = ({ children }) => {
  const left = React.cloneElement(children[0], {
    style: { ...children[0].props.style, flex: "1", marginRight: "10px" }
  });
  const right = React.cloneElement(children[1], {
    style: { ...children[1].props.style, flex: "1" }
  });

  return (
    <div style={{ display: "flex" }}>
      {left}
      {right}
    </div>
  );
};

const App: React.FC = () => (
  <TwoColumnLayout>
    <div style={{ background: "blue" }}>left</div>
    <div style={{ background: "red" }}>right</div>
  </TwoColumnLayout>
);

React.cloneElementを使わないやり方

React.cloneElementを使わずに実現しようとすると以下のようになります。

interface Props {
  children: [React.ReactElement, React.ReactElement];
}

const TwoColumnLayout: React.FC<Props> = ({ children }) => {
  return (
    <div style={{ display: "flex" }}>
      <div style={{ flex: "1", marginRight: "10px" }}>{children[0]}</div>
      <div style={{ flex: "1" }}>{children[1]}</div>
    </div>
  );
};

React.cloneElementを使った場合と比較すると、こちらのほうがコードとしては簡潔です。

しかしflex:1を与えるためにchildrenをラップするdivタグが必要になります。
余計なdivタグが増えてしまうことを許容できるのであれば、このやり方でもよいと思います。

私は余分なdivタグが増えるのは好きではないので、React.cloneElementを使ったやり方でいつも書いています。

31
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
13