背景
Webアプリケーション開発中にReact+TypeScriptで下記のようなイメージのページを作りたいと思ったのですが,子コンポーネントのStateの変化(ここでは,「表示数セレクタが選択されること」
)を親コンポーネントのレンダリングされる別コンポーネント(ここでは,card
)の表示数に反映させる方法がわからず,調査したので,メモとして残します.
(補足)今回やろうとしていることは,上記のイメージ図にあるように,子コンポーネントである表示数変更用のセレクタの変更をindexページ(親コンポーネント)に渡し,変更されたStateに応じて同じく子コンポーネントのcardコンポーネントの表示数をコントロールする実装です.
環境
TypeScript 4.9.4
React 18.2.0
(なお,webアプリケーションフレームワークには Ruby on Rails を使用しています)
実装
ポイントはpropsにState更新用のメソッド(setValue
)を設定して,親コンポーネントから更新したいState(count
)を更新するメソッド(setCount
)を渡してあげることです.
-
子コンポーネント
- セレクタの実装はほとんどMaterial UIの実装をまねしました.
React Select component - Material UI
BasicSelect.tsximport * as React from 'react'; import Box from '@mui/material/Box'; import InputLabel from '@mui/material/InputLabel'; import MenuItem from '@mui/material/MenuItem'; import FormControl from '@mui/material/FormControl'; import Select, { SelectChangeEvent } from '@mui/material/Select'; type Props = { value: number|string, // state setValue // callback (stateを変更するためのprops) options: Array<number | string> }; export const BasicSelect = ({value, options, setValue}: Props) => { const handleChange = (event: SelectChangeEvent) => { setValue(event.target.value); //親コンポーネントへのstateの引き渡し }; return ( <Box> <FormControl variant="filled" sx={{ m: 1, minWidth: 150 }}> <InputLabel id="select-label">表示数</InputLabel> <Select labelId="select-label" id="select" value={value} label="count" onChange={handleChange} > {options.map((option) => <MenuItem key={option} value={option}>{option}</MenuItem>)} </Select> </FormControl> </Box> ); }
- セレクタの実装はほとんどMaterial UIの実装をまねしました.
-
親コンポーネント
ここでのカードの実装やindexページの実装もMaterial UIを頼らせていただきました.
いくつか割愛している箇所もあるので,気になる方は下記のドキュメントをご覧ください.
React Grid component - Material UI
React Card component - Material UIindex.tsximport { MyCard } from 'app/javascript/components/MyCard/MyCard'; //表示するカードコンポーネント(ここの実装は割愛します.詳細は参考文献をご覧ください) import { BasicSelect } from 'app/javascript/components/BasicSelect/BasicSelect'; //子コンポーネントのインポート import { Grid, Box} from '@mui/material'; import React from 'react'; import ReactDOM from 'react-dom'; export const Index = () => { const display_count_options = [1, 2, 3, 4]; //selector options const [count, setCount] = React.useState(2); //state変数,初期値と更新のための関数を宣言 const { card_data } = getCardData(); //表示するCardに記載する内容を配列で取得(ここの実装は割愛します.詳細は参考文献をご覧ください) return ( <> <BasicSelect value={count} setValue={setCount} options={display_count_options} /> <br/> <Box sx={{ flexGrow: 1 }}> <Grid container spacing={2}> {card_data.slice(0,count).map((data) => <Grid item xs={4} sx={{ padding: 0 }} key={data.name}> <MyCard/> </Grid> )} </Grid> </Box> </> ); }; ReactDOM.render(<Index />, document.getElementById('Index'));
結果
思惑通り,セレクタの選択内容に応じてカードの表示数を変更できるようになりました.
参考文献
- indexページの実装(CardとGridを組み合わせて表示するには...)
Material UIでカードを作成してグリッドで整理する方法|廻遊者!!