どうも、ぽっぽです。
GitHub – microsoft\frontend-bootcamp: Frontend Workshop from HTML\CSS\JS to TypeScript\React\Reduxを参考にReactのContext APIについて理解したことを紹介します。
Reactのルールに従うと、アプリケーションが複雑になればなるほどある問題に直面します。
- データは、コンポーネント間を
Props
を使って上のコンポーネントから下のコンポーネントに伝えます。中間に位置するコンポーネントでは使わないけど、更に下のコンポーネントで使う場合も中間のコンポーネントにProps
を渡す必要があります。この問題をprops drillingと言います。 - 共有データは、ユーザ操作やネットワークの更新によって変化します。コンポーネント間に変更を伝搬するのが困難になります。
この問題を解決する方法の一つにCotext APIがあります。
Todoアプリのコンポーネント間のイメージ
interface TodoListProps {
todos: Todos;
filter: FileTypes;
complete: (id: string) => void;
edit: (id: string, label: string) => void;
remove: (id: string) => void;
}
<TodoList>
はインターフェイスでProps
を上記の型で受け取ります。
<TodoListItem key={id} id={id} complete={complete} todos={todos} edit={edit} remove={remove}></TodoListItem>
そしてこのように、<TodoListItem>
にProps
を受け渡します。
このような中間のコンポーネントが増えると複雑さが増します。
Context APIでTodoアプリを書いたソース
Reactで作成したTodoアプリにContext APIを使ったソースをこちらからダウンロード出来ます。
このソースはCreate React App · Set up a modern web app by running one command.を使っています。複数のツールを組み合わせて必要な設定なども行ってくれているので便利でした。
npm install
npm start
Todoアプリを動作させる場合、NodeJSのコマンドプロンプトを開いて、ソースをダウンロードして展開した場所(package.json
ファイルがある)に移動します。1行目のコマンドを実行します。package.json
のパッケージをインストールして、インストールが終わるまで数十秒ほど待ちます。
2行目のコマンドを実行するとブラウザにTodoアプリが表示されます。
終わるときは、コマンドプロンプト上でCtrl + C
をタイプします。
Context APIの使い方
createContext
メソッドでContext
を作成します。- 作成された
Context
のProvider
を使ってコンポーネントを挟み込みます。 class component
ではthis.context
でアクセス出来ます。function componet
ではuseContext
hookを使ってContext
にアクセスします。
// contextを作成
const TodoContext = React.createContext(undefined);
class TodoApp extends React.Component {
render() {
// ProviderでPropsを指定
return (
<TodoContext.Provider
value={{
...this.state,
addTodo: this._addTodo,
setFilter: this._setFilter,
/* same goes for remove, complete, and clear */
}}>
<div>
<TodoHeader />
<TodoList />
<TodoFooter />
</div>
</TodoContext.Provider>
);
}
}
//class componentでcontextを参照する例
class TodoHeader extends React.Component {
render() {
// Step 1: contextでpropにアクセス
return <div>Filter is {this.context.filter}</div>;
}
}
// Step 2: componet classのcontextTypeプロパティをセットする
TodoHeader.contextType = TodoContext;
//function componentでcontextを参照する例
const TodoFooter = props => {
const context = useContext(TodoContext);
return (
<div>
<button onClick={context.clear()}>Clear Completed</button>
</div>
);
};
これで必要なデータをContext
を経由してアクセス出来るようになり、中間のコンポーネントを経由する必要はなくなります。