React 関数コンポーネント間での props の受け渡し


# 初めに

関数コンポーネント間での props の流れを理解するために、サンプルコードや図を入れて自分なりに整理してみました。

# 1. コンポーネントに変数を渡す

変数 `morning`、`hello`、`evening` を `BasicTabs` コンポーネントに渡します。

```react:App.js
const morning =  <p>Good Morning!</p>;
const hello =  <p>Hello!</p>;
const evening =  <p>Good Evening!</p>;

const App = () => {
  return (
    <div className="App">
      {/* 変数 morning、hello、evening を BasicTabs コンポーネントに渡す */}
      <BasicTabs morning={morning} hello={hello} evening={evening} />
    </div>
  );
}

export default App;
```

```react:TabSample.js
const BasicTabs = props => {
  // 親コンポーネントである App コンポーネントから props を受け取る
  const { morning, hello, evening } = props;

  return (
    <Box sx={{ width: '100%' }}>
      <TabPanel value={value} index={0}>
        {morning}
      </TabPanel>
      <TabPanel value={value} index={1}>
        {hello}
      </TabPanel>
      <TabPanel value={value} index={2}>
        {evening}
      </TabPanel>
    </Box>
  );
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/8d86b232-1ea4-29ae-2d97-40c855bd8e6e.png)

完全なコードはこちらです。

https://github.com/quryu-1/react-component-example/tree/main/example/example-1

# 2. コンポーネントにコンポーネントを渡す

関数コンポーネント `Morning`、`Hello`、`Evening` を `BasicTabs` コンポーネントに渡します。

```react:App.js
const Morning = () => <p>Good Morning!</p>;
const Hello = () => <p>Hello!</p>;
const Evening = () => <p>Good Evening!</p>;

const App = () => {
  return (
    <div className="App">
      {/* 関数コンポーネント Morning、Hello、Evening を BasicTabs コンポーネントに渡す */}
      <BasicTabs Morning={Morning} Hello={Hello} Evening={Evening} />
    </div>
  );
}

export default App;
```

```react:TabSample.js
const BasicTabs = props => {
  const { Morning, Hello, Evening } = props;

  return (
    <Box sx={{ width: '100%' }}>
      <TabPanel value={value} index={0}>
        <Morning />
      </TabPanel>
      <TabPanel value={value} index={1}>
        <Hello />
      </TabPanel>
      <TabPanel value={value} index={2}>
        <Evening />
      </TabPanel>
    </Box>
  );
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/ba1d8759-fa40-ffe1-62e6-89689d82032e.png)

完全なコードはこちらです。

https://github.com/quryu-1/react-component-example/tree/main/example/example-2

# 3. コンポーネントに state を持つコンポーネントを渡す

`Button` 要素を追加し、クリックするとカウントアップするような state をそれぞれ `Morning` コンポーネント、`Hello` コンポーネント、`Evening` コンポーネントに追加しました。

```react:App.js
const Morning = () => {
  // カウントアップ用の state を定義
  const [count, setCount] = useState(0);
  return (
    <>
      <p>Good Morning! {count}</p>
      {/* ボタンをクリックすると、count を count + 1 で更新する */}
      <Button variant="contained" onClick={() => setCount(count + 1)}>Click</Button>
    </>
  );
}
  return (
    <>
      <p>Good Morning! {count}</p>
      <Button variant="contained" onClick={() => setCount(count + 1)}>Click</Button>
    </>
  );
}

const Hello = () => {
  const [count, setCount] = useState(0);
  return (
    <>
      <p>Hello! {count}</p>
      <Button variant="contained" onClick={() => setCount(count + 1)}>Click</Button>
    </>
  );
}

const Evening = () => {
  const [count, setCount] = useState(0);
  return (
    <>
      <p>Good Evening! {count}</p>
      <Button variant="contained" onClick={() => setCount(count + 1)}>Click</Button>
    </>
  );
}

const App = () => {
  return (
    <div className="App">
      <BasicTabs Morning={Morning} Hello={Hello} Evening={Evening} />
    </div>
  );
}

export default App;
```

TabSample.js に変更はありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/f1c9ebb6-ff6b-ec50-4882-bfb7e88a7394.png)

完全なコードはこちらです。

https://github.com/quryu-1/react-component-example/tree/main/example/example-3

# 4. コンポーネントの共通部分を別のコンポーネントで定義する

`Morning`、`Hello`、`Evening` コンポーネントには共通化できる部分があります。それを別コンポーネントとして定義します。

```react:App.js
const Morning = () => <Greeting greeting={"Good Morning!"}/>;
const Hello = () => <Greeting greeting={"Hello!"}/>;
const Evening = () => <Greeting greeting={"Good Evening!"}/>;

const Greeting = props => {
  const { greeting } = props;
  const [count, setCount] = useState(0);
  return (
    <>
      {/* 挨拶要素とボタン要素を共通化 */}
      <p>{greeting} {count}</p>
      <Button variant="contained" onClick={() => setCount(count + 1)}>Click</Button>
    </>
  );
}

const App = () => {
  return (
    <div className="App">
      <Grid container>
        <Grid item xs={6}>
          <BasicTabs Morning={Morning} Hello={Hello} Evening={Evening} />
        </Grid>
      </Grid>
    </div>
  );
}

export default App;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/3b4d1bbc-32a3-1954-6e6a-b31bddfdf73c.png)

完全なコードはこちらです。

https://github.com/quryu-1/react-component-example/tree/main/example/example-4

# 5. state の移動

前章ではタブを切り替えるとカウントが 0 にリセットされてしまいます。これはタブ切り替え時に `Morning`、`Hello`、`Evening` コンポーネントが初期化されるためです。タブを切り替えてもカウントを保持したい場合は、親コンポーネントである `BasicTabs` でそれぞれのカウントを定義します。子コンポーネントに state を渡し、その子コンポーネントで state の更新を行い、その更新を親コンポーネントに反映させる場合、更新用関数も渡します。

```react:TabSample.js
const BasicTabs = () => {
  // ここで count を定義する
  const [morningCount, setMorningCount] = useState(0);
  const [helloCount, setHelloCount] = useState(0);
  const [eveningCount, setEveningCount] = useState(0);

  return (
    <Box sx={{ width: '100%' }}>
      <TabPanel value={value} index={0}>
        {/* count を渡す */}
        <Morning count={morningCount} setCount={setMorningCount}/>
      </TabPanel>
      <TabPanel value={value} index={1}>
        <Hello count={helloCount} setCount={setHelloCount}/>
      </TabPanel>
      <TabPanel value={value} index={2}>
        <Evening count={eveningCount} setCount={setEveningCount}/>
      </TabPanel>
    </Box>
  );
}
```

上記のように定義した state を `Morning`、`Hello`、`Evening` コンポーネントに props として渡し、以下のように受け取ります。

```react:TabSample.js
const Morning = props => {
  // 親コンポーネントである BasicTabs コンポーネントから props を受け取る
  const { count, setCount } = props;
  return (
    <>
      <p>Good Morning! {count}</p>
      <Button variant="contained" onClick={() => setCount(count + 1)}>Click</Button>
    </>
  );
}

const Hello = props => {
  const { count, setCount } = props;
  return (
    <>
      <p>Hello! {count}</p>
      <Button variant="contained" onClick={() => setCount(count + 1)}>Click</Button>
    </>
  );
}

const Evening = props => {
  const { count, setCount } = props;
  return (
    <>
      <p>Good Evening! {count}</p>
      <Button variant="contained" onClick={() => setCount(count + 1)}>Click</Button>
    </>
  );
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/5ec5ba44-ebc6-6cf4-55e4-7fa38837f7c4.png)


完全なコードはこちらです。

https://github.com/quryu-1/react-component-example/tree/main/example/example-6

# 6. コンポーネントの共通部分を別のコンポーネントで定義する（再）

[4. コンポーネントの共通部分を別のコンポーネントで定義する](#4.コンポーネントの共通部分を別のコンポーネントで定義する) で行った共通化を再び行います。`Morning`、`Hello`、`Evening` コンポーネントで共通している state と state 更新関数、後は挨拶言葉を props として受け取るようにします。

```react:TabSample.js
const Greeting = props => {
  const { greeting, count, setCount } = props;
  return (
    <>
      <p>{greeting} {count}</p>
      <Button variant="contained" onClick={() => setCount(count + 1)}>Click</Button>
    </>
  );
}
```

これを利用すれば、`Morning`、`Hello`、`Evening` コンポーネントはそれぞれ以下のように簡潔に書くことができます。

```react:TabSample.js
const Morning = props => <Greeting greeting="Good Morning!" {...props} />;
const Hello = props => <Greeting greeting="Hello!" {...props} />;
const Evening = props => <Greeting greeting="Good Evening!" {...props} />;
```

※`{...props}` は、`Morning`、`Hello`、`Evening` コンポーネントが親コンポーネントから受け取っている props （カウント用の state、カウント更新用関数）をそのまま子コンポーネントに渡しています。省略せずに書けば、以下のようになります。

```react
const Morning = props => <Greeting greeting="Good Morning!" count={props.count} setCount={props.setCount} />;
const Hello = props => <Greeting greeting="Hello!" count={props.count} setCount={props.setCount} />;
const Evening = props => <Greeting greeting="Good Evening!" count={props.count} setCount={props.setCount} />;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/e1f18fb3-5320-2599-abe9-ff1aea1bfa56.png)


完全なコードはこちらです。

https://github.com/quryu-1/react-component-example/tree/main/example/example-7

# 7. もっと簡単にしたい

そもそも `Morning`、`Hello`、`Evening` コンポーネントを定義する必要はありませんでした。

```react:TabSample.js
const Greeting = props => {
  const { greeting, count, setCount } = props;
  return (
    <>
      <p>{greeting} {count}</p>
      <Button variant="contained" onClick={() => setCount(count + 1)}>Click</Button>
    </>
  );
}

const BasicTabs = () => {
  const [morningCount, setMorningCount] = useState(0);
  const [helloCount, setHelloCount] = useState(0);
  const [eveningCount, setEveningCount] = useState(0);

  return (
    <Box sx={{ width: '100%' }}>
      <TabPanel value={value} index={0}>
        {/* 共通化コンポーネントをここに直接書く */}
        <Greeting greeting="Good Morning!" count={morningCount} setCount={setMorningCount}/>
      </TabPanel>
      <TabPanel value={value} index={1}>
        <Greeting greeting="Hello!" count={helloCount} setCount={setHelloCount}/>
      </TabPanel>
      <TabPanel value={value} index={2}>
        <Greeting greeting="Good Evening!" count={eveningCount} setCount={setEveningCount}/>
      </TabPanel>
    </Box>
  );
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/6b2fa9ed-049b-bb56-bff3-39415df99065.png)


完全なコードはこちらです。

https://github.com/quryu-1/react-component-example/tree/main/example/example-8

# 8. state の移動（再）

今度は `App` コンポーネントまで state を移動させます。

```react:App.js
const App = () => {
  // ここでカウント用の state を定義
  const [morningCount, setMorningCount] = useState(0);
  const [helloCount, setHelloCount] = useState(0);
  const [eveningCount, setEveningCount] = useState(0);

  return (
    <div className="App">
      <Grid container spacing={2}>
        <Grid item xs={6}>
          <BasicTabs
            morningCount={morningCount}
            setMorningCount={setMorningCount}
            helloCount={helloCount}
            setHelloCount={setHelloCount}
            eveningCount={eveningCount}
            setEveningCount={setEveningCount}
          />
        </Grid>
      </Grid>
    </div>
  );
}
```

```react:TabSample.js
const BasicTabs = props => {
  const {
    morningCount, setMorningCount,
    helloCount, setHelloCount,
    eveningCount, setEveningCount,
  } = props;

  return (
    <Box sx={{ width: '100%' }}>
      <TabPanel value={value} index={0}>
        <Greeting greeting="Good Morning!" count={morningCount} setCount={setMorningCount}/>
      </TabPanel>
      <TabPanel value={value} index={1}>
        <Greeting greeting="Hello!" count={helloCount} setCount={setHelloCount}/>
      </TabPanel>
      <TabPanel value={value} index={2}>
        <Greeting greeting="Good Evening!" count={eveningCount} setCount={setEveningCount}/>
      </TabPanel>
    </Box>
  );
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/331996ac-767e-e778-d9d5-a61b41fee6cc.png)


完全なコードはこちらです。

https://github.com/quryu-1/react-component-example/tree/main/example/example-9

# 9. App コンポーネントに別の子コンポーネントを追加する

[8. state の移動（再）](#8.stateの移動（再）)で App コンポーネントにカウント用 state を移動したので、App コンポーネントの子コンポーネントでカウント用 state を props として渡すことができます。ここでは各カウントのサマリーを表示する子コンポーネントを作りました。。

```react:App.js
const App = () => {
  const [morningCount, setMorningCount] = useState(0);
  const [helloCount, setHelloCount] = useState(0);
  const [eveningCount, setEveningCount] = useState(0);

  return (
    <div className="App">
      <Grid container >
        <Grid item xs={6}>
          <BasicTabs
            morningCount={morningCount}
            setMorningCount={setMorningCount}
            helloCount={helloCount}
            setHelloCount={setHelloCount}
            eveningCount={eveningCount}
            setEveningCount={setEveningCount}
          />
        </Grid>
        <Grid item xs={6}>
          <Summmary
            morningCount={morningCount}
            helloCount={helloCount}
            eveningCount={eveningCount}
          />
        </Grid>
      </Grid>
    </div>
  );
}
```

```react:Summary.js
const Summary = props => {
  const { morningCount, helloCount, eveningCount } = props;
  return (
    <>
      <Typography variant="h5">Greeting count</Typography>
      <Grid container spacing={2}>
        <Grid item xs={4}>Morning Count</Grid>
        <Grid item xs={4}>Hello Count</Grid>
        <Grid item xs={4}>Evening Count</Grid>
        <Grid item xs={4}>{morningCount}</Grid>
        <Grid item xs={4}>{helloCount}</Grid>
        <Grid item xs={4}>{eveningCount}</Grid>
      </Grid>
    </>
  );
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/01b4856b-bf23-5d01-1f81-da1e347620c8.png)

![React-App-Google-Chrome-2022-06-18-19-33-18.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/800e46d2-7714-842d-eab4-eeb1d45fc244.gif)

# 参考記事

https://ja.reactjs.org/docs/introducing-jsx.html

https://ja.reactjs.org/docs/components-and-props.html

https://mui.com/material-ui/react-tabs/