useState フックでデータを更新する Provider と Consumer を実装する
この記事では「React Context API を使う簡単な例」から「useState フックを用いてステートにデータを置く」にかけて作ったプログラムを変更して、 ステートに保存したデータを更新可能にします。
このプログラムは、リスト内のアイテムをクリックすると削除でき、Add ボタンをクリックすると Fruit 番号 というアイテムが追加されるという動きとします。
Provider に state 更新用のメソッドを渡す
前回の記事「useState フックを用いてステートにデータを置く」では、useState フックを用いて state にデータをセットしました。
これを発展させて、データを更新できるようにしましょう。
FruitContext.jsを次のように変更します。
import React, { createContext, useState } from 'react';
import { fruitData } from './fruit-data';
const FruitContext = createContext();
export const FruitProvider = ({ children }) => {
const [fruits, setFruits] = useState(fruitData);
const addFruit = (newFruit) => setFruits([...fruits, newFruit]);
const removeFruit = (s) => setFruits(fruits.filter((fruit) => fruit !== s));
return (
<FruitContext.Provider value={{ fruits, addFruit, removeFruit }}>
{children}
</FruitContext.Provider>
);
};
export const FruitConsumer = FruitContext.Consumer;
useState フックは state の初期値を受け取り、 「state の値を保持する変数」と「state を更新する関数」を要素に持つ配列を返します。
7 行目では、useState フックが返す配列のひとつ目の要素 (つまり「state の値を保持する変数」) を fruitsに、 二つ目の要素 (つまり「state を更新する関数」) を setFruits という名前の定数にセットしています。
8-9 行目では、setFruits関数を用いて、addFruits 関数と removeFruit 関数を実装しています。
12 行目で Provider の value プロパティに、fruits (データ) の他に、state 更新用の関数、つまり addFruit と removeFruit を渡しています。
これによって、Consumer 側で state データ以外に、データ更新用のメソッドを取り出すことができます。
データ更新可能な Consumer の実装
Provider 側でデータ更新用の関数を渡しているので、Consumer 側でそれを利用するのは簡単です。
App.js は次のようになります。
import React from 'react';
import './App.css';
import { FruitConsumer } from './FruitContext';
function App() {
return (
<FruitConsumer>
{(value) => {
const { fruits, addFruit, removeFruit } = value;
return (
<>
<h1>Fruits</h1>
<div>
<button onClick={() => addFruit(`Fruit ${fruits.length + 1}`)}>
Add
</button>
</div>
<ul>
{fruits.map((fruit, i) => (
<li key={i} onClick={() => removeFruit(fruit)}>
{fruit}
</li>
))}
</ul>
</>
);
}}
</FruitConsumer>
);
}
export default App;
9 行目で Provider が渡す value から、state のデータ fruits の他に、 addFruit 関数、removeFruit 関数を受け取っています。
JavaScript の復習
Provider でデータを渡す側はショートハンドプロパティ、Consumer で受け取る側は分割代入です。どちらも React の機能ではなく JavaScript (ES6+) のシンタックスです。 React 固有のシンタックスではありません。丸暗記にならないように気を付けましょう。
14 行目では新しく追加したボタン要素の、クリックハンドラで addFruit 関数を呼び出しています。
20 行目ではリストアイテムのクリックハンドラで、クリックしたアイテムと同じ名前の要素を、state データの配列の中でフィルターして削除しています。
以上で、データの更新を行うことができました。
これで動作は問題ないのですが、さらに useContext フックを用いることで、コンスーマはもっと簡略化できます。 useContext を用いた例は「useContext フックで Consumer をリファクタリングする」で説明します。