初めに
関数コンポーネント間での props の流れを理解するために、サンプルコードや図を入れて自分なりに整理してみました。
1. コンポーネントに変数を渡す
変数 morning
、hello
、evening
を BasicTabs
コンポーネントに渡します。
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;
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>
);
}
完全なコードはこちらです。
2. コンポーネントにコンポーネントを渡す
関数コンポーネント Morning
、Hello
、Evening
を BasicTabs
コンポーネントに渡します。
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;
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>
);
}
完全なコードはこちらです。
3. コンポーネントに state を持つコンポーネントを渡す
Button
要素を追加し、クリックするとカウントアップするような state をそれぞれ Morning
コンポーネント、Hello
コンポーネント、Evening
コンポーネントに追加しました。
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 に変更はありません。
完全なコードはこちらです。
4. コンポーネントの共通部分を別のコンポーネントで定義する
Morning
、Hello
、Evening
コンポーネントには共通化できる部分があります。それを別コンポーネントとして定義します。
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;
完全なコードはこちらです。
5. state の移動
前章ではタブを切り替えるとカウントが 0 にリセットされてしまいます。これはタブ切り替え時に Morning
、Hello
、Evening
コンポーネントが初期化されるためです。タブを切り替えてもカウントを保持したい場合は、親コンポーネントである BasicTabs
でそれぞれのカウントを定義します。子コンポーネントに state を渡し、その子コンポーネントで state の更新を行い、その更新を親コンポーネントに反映させる場合、更新用関数も渡します。
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 として渡し、以下のように受け取ります。
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>
</>
);
}
完全なコードはこちらです。
6. コンポーネントの共通部分を別のコンポーネントで定義する(再)
4. コンポーネントの共通部分を別のコンポーネントで定義する で行った共通化を再び行います。Morning
、Hello
、Evening
コンポーネントで共通している state と state 更新関数、後は挨拶言葉を props として受け取るようにします。
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
コンポーネントはそれぞれ以下のように簡潔に書くことができます。
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、カウント更新用関数)をそのまま子コンポーネントに渡しています。省略せずに書けば、以下のようになります。
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} />;
完全なコードはこちらです。
7. もっと簡単にしたい
そもそも Morning
、Hello
、Evening
コンポーネントを定義する必要はありませんでした。
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>
);
}
完全なコードはこちらです。
8. state の移動(再)
今度は App
コンポーネントまで state を移動させます。
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>
);
}
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>
);
}
完全なコードはこちらです。
9. App コンポーネントに別の子コンポーネントを追加する
8. state の移動(再)で App コンポーネントにカウント用 state を移動したので、App コンポーネントの子コンポーネントでカウント用 state を props として渡すことができます。ここでは各カウントのサマリーを表示する子コンポーネントを作りました。。
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>
);
}
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>
</>
);
}
参考記事