Reactにおける子コンポーネントのステートを介した動作モード
製品の配列が次のようになっているとします:
const initProds = [
{id: id(), name: 'prod1', cost: 'cost1', catg: 'catg1'},
{id: id(), name: 'prod2', cost: 'cost2', catg: 'catg2'},
{id: id(), name: 'prod3', cost: 'cost3', catg: 'catg3'},
];
これらの製品をHTMLテーブルとして表示してみましょう。
また、テーブルの任意のセルをクリックすると、そのセルに編集用の入力フィールドが表示されるようにします。
この問題を解決するために、3つのコンポーネントを作成します。
Productsコンポーネントは製品のステートを保持し、
製品を表示するためにProductコンポーネントを使用します。
Productコンポーネントは、今度は特定の製品フィールド(名前、価格、カテゴリ)を表示するために
ProductFieldコンポーネントを使用します。
ProductFieldコンポーネントは、
フィールドのテキストを表示するか、その編集用の入力フィールドを表示します。
このとき、編集モードまたは表示モードはこのコンポーネント自身のステートによって制御されるようにします。
つまり、モードを親のステートに保存しません。 親に保存すると非常に不便になります。なぜなら、製品の各フィールドごとにモードを指定する必要があり、 ステートが次のようなものになってしまうからです:
const initProds = [
[
{field: 'name', value: 'prod1', isEdit: false},
{field: 'cost', value: 'cost1', isEdit: false},
{field: 'catg', value: 'catg1', isEdit: false},
],
[
{field: 'name', value: 'prod2', isEdit: false},
{field: 'cost', value: 'cost2', isEdit: false},
{field: 'catg', value: 'catg2', isEdit: false},
],
[
{field: 'name', value: 'prod3', isEdit: false},
{field: 'cost', value: 'cost3', isEdit: false},
{field: 'catg', value: 'catg3', isEdit: false},
],
]
しかし、私たちはそのようなステートを作らず、元のステートをそのままにします。
単に、各ProductFieldコンポーネントのインスタンスが、自身のステートを使って、
編集モードか表示モードかを制御するようにします。
これにより、親コンポーネントがデータのステートを保持し、 孫コンポーネントがそのデータをpropsを通じて受け取りながら、 自身のモードを変更するための独自のステートを持つ、という構造になります。
では、説明した内容を実装してみましょう。
Productsコンポーネント
function Products() {
const [prods, setProds] = useState(initProds);
function changeField(id, field, event) {
setProds(prods.map(prod => {
if (prod.id === id) {
prod[field] = event.target.value;
}
return prod;
}));
}
const rows = prods.map(prod => {
return <Product
key ={prod.id}
id ={prod.id}
name={prod.name}
cost={prod.cost}
catg={prod.catg}
changeField={changeField}
/>;
});
return <div>
<table>
<tbody>
{rows}
</tbody>
</table>
</div>;
}
Productコンポーネント
function Product({ id, name, cost, catg, changeField }) {
return <tr>
<ProductField id={id} text={name} type="name" changeField={changeField} />
<ProductField id={id} text={cost} type="cost" changeField={changeField} />
<ProductField id={id} text={catg} type="catg" changeField={changeField} />
</tr>;
}
ProductFieldコンポーネント
function ProductField({ id, text, type, changeField }) {
const [isEdit, setIsEdit] = useState(false);
return <td>
{
isEdit
? <input
value={text}
onChange={event => changeField(id, type, event)}
onBlur={() => setIsEdit(false)}
/>
: <span onClick={() => setIsEdit(true)}>{text}</span>
}
</td>;
}
実践タスク
これまでのレッスンで作成したUsersコンポーネントとUserコンポーネントに対しても、
同様の操作を実装してみてください。