はじめに
今回はReact Hooksについてのまとめ記事の最終回になります。
useReducer
stateとdispatch(actionを送信する関数)を返すフックです。
このフックを利用すれば、コンポーネント内でstate管理ができます。
そのためuseStateと似たような機能を持つフックです。
構文
const [state, dispatch] = useReducer(reducer, stateの初期値);
戻り値のdispatchにactionを渡すとstateが更新されます。
引数のreducerによって更新のされ方が変わります。
actionとは...
アクション(何が起きたか)ととそれに付随する情報を持つオブジェクトのこと。
例
const action = {
type: 'ADD_TODO', // アクションタイプ
text: 'Learning React' // 付随情報
};
// useReducerの返り値のdispatchにactionを渡すと、actionが送信さてreducerが実行される
// actionを送信することを「actionをdispatchすると言います」
dispatch(action);
reducerとは...
現在のstateとactionを受け取り、actionに応じて更新したstateを返す関数。
actionをdispatchするとreducerが実行される。
例
// アクションタイプがADD_TODOのactionがdispatchされた時、stateを更新して返す
// useReducerの第一引数にこのようなものを渡す想定
function reducer(state, action) {
switch(action.type) {
case 'ADD_TODO':
return [...state, [{ text: action.text, completed:false }]];
default:
return state;
}
}
使用例
import React, { useReducer } from 'react';
// 現在の state と action を受け取り、action に応じて更新した state を返す関数
function reducer(state, action) {
switch (action.type) {
case 'INCEREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
case 'RESET':
return { count: 0 };
default:
return state;
}
}
export default function App() {
// useReducerの第2引数に { count: 0 } を渡しているので、state の初期値は { count: 0 }
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<>
<p>count: {state.count}</p>
{/* { type: 'DECREMENT' } という action を送信する */}
<button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>
{/* { type: 'INCEREMENT' } という action を送信する */}
<button onClick={() => dispatch({ type: 'INCEREMENT' })}>+</button>
{/* { type: 'RESET' } という action を送信する */}
<button onClick={() => dispatch({ type: 'RESET' })}>reset</button>
</>
);
}
useReducerとuseStateとの使い分け
複雑なstateを扱う時はuseReducerを利用した方が良いです。
それ以外は基本useStateで問題ないです。
「複雑なstate」というのは、下記のように別のstateに依存しているstateを扱う場合を想定しています。
・stateを更新するために、別のstateを参照する必要がある
・stateを更新したら、別のstateも一緒に更新する必要がある
・stateを更新するロジックが複雑
useContext
ReactにおけるContextとは...
以下のいずれかを指している
①Propsを利用せずに様々な階層のコンポーネントに値を共有するReactの仕組み
②Propsを利用せずに様々な階層のコンポーネントに値を共有するReactのAPI
③Contextオブジェクトのこと
④Contextオブジェクトの値のこと
今回は①の意味で話を進めます。
Contextを使用するには...
以下が必要
①Contextオブジェクト
->React.createContextの戻り値を指す
②Provider
->Contextオブジェクトが保持しているコンポーネントを指す(Providerコンポーネントとも呼ぶ)
③Consumer
->Contextオブジェクトから値を首都区しているコンポーネントを指す(Consumerコンポーネントとも呼ぶ)
importReact,{createContext,useContext}from'react';
// ①Contextオブジェクト
const MyContext = createContext();
// ③MyContextから値を取得しているのでConsumerである
functionChild1(){
//<MyContext.Providervalue={name}>のnameは'React'なので、textの値は'React'になる。
const text = useContext(MyContext);
return <h1>{text}</h1>;
}
// MyContextから値を取得していないのでConsumerではない
functionChild2(){
return <h2>NotConsumer</h2>;
}
export defaultfunctionApp(){
constname='React';
return(
{/*②Provider。valueプロパティの値をConsumerに共有する。*/}
<MyContext.Providervalue={name}>
<Child1/>
<Child2/>
</MyContext.Provider>
);
}
Contextを使う理由
①グローバルなstateを作成できるため
②Prop drilling問題(バケツリレー問題)を解決できるため
useContextとは
Contextオブジェクトから値を取得するフック。
Providerから共有される値を取得するために必要。
簡単にいうと、親からPropsで渡されていないのに、Contextに収容されているデータへよりシンプルにアクセスできる機能です。
構文
const Context オブジェクトの値 = useContext(Context オブジェクト);
Contextオブジェクトから取得できる値はContextオブジェクトが保持しているProviderのvalueプロパティの値になります。
使用例
import React, { useState, useContext, createContext } from "react";
// Context オブジェクト
const MyContext = createContext();
export default function App() {
const [count, setCount] = useState(0);
const value = {
name: "soarflat",
handleClick: () => setCount((count) => count + 1)
};
return (
<div>
<p>count: {count}</p>
{/* Provider。value プロパティの値を共有する。 */}
<MyContext.Provider value={value}>
<Child />
</MyContext.Provider>
</div>
);
}
function Child() {
return <GrandChild />;
}
function GrandChild() {
return <GreatGrandChild />;
}
function GreatGrandChild() {
// Provider(<MyContext.Provider value={value}>)から
// Context オブジェクトの値(value プロパティの値)を取得する。
// そのため、context は
// {
// name: 'soarflat',
// handleClick: () => setCount(count => count + 1)
// }
// になる。
const context = useContext(MyContext);
return (
<>
<p>{context.name}</p>
<button onClick={context.handleClick}>increment</button>
</>
);
}
Contextを使うデメリット
下記が考えられるので無闇に使わず、使い所を見極めることがポイント。
①コンポーネントがContextに依存するため、コンポーネントの再利用性が低下する
②Contextオブジェクトの値はグローバルなstateのため、下手に使うとどこで利用されているかがわかりにくくなる
カスタムフック
自作のフックのことを指す。
具体的にいうと、コンポーネントから切り出したロジックを定義した関数です。
カスタムフックのメリット
①コンポーネント内のロジックを切り出すことで複数のコンポーネントで再利用できる
②コンポーネントから複雑なロジックを切り出すとコンポーネントの見通しが良くなる
使用例
import React, { useState } from "react";
// カスタムフック
// カスタムフックの名前はuseから始まる必要があります。
function useCounter(initialCount) {
const [count, setCount] = useState(initialCount);
const increment = () => {
setCount(count + 1);
};
const decrement = () => {
setCount(count - 1);
};
return { count, increment, decrement };
}
// 再利用①
function Counter1() {
const { count, increment } = useCounter(0);
return (
<>
<p>count: {count}</p>
<button onClick={increment}>+</button>
</>
);
}
// 再利用②
function Counter2() {
const { count, decrement } = useCounter(10);
return (
<>
<p>count: {count}</p>
<button onClick={decrement}>-</button>
</>
);
}
function App() {
return (
<div>
<Counter1 />
<Counter2 />
</div>
);
}
export default App;