React hooksのuseReducerについて勉強した。
useReducerとは
複雑な状態遷移を処理するのに最適。逆にいうと数が少ない場合なuseStateかな。 この機能がでる前まではReduxを使用していました。
const [state, dispatch] = useReducer(reducer,'初期値')
reducerはstateを更新するための関数で、dispatchは、reducerを実行するための呼び出し関数です。 (変数を宣言するときに、stateの更新方法をあらかじめ設定しておくことが出来る。)
useStateとuseReducerの比較
比較してくれていたページがあったの引用させてもらいました
https://qiita.com/seira/items/2fbad56e84bda885c84c より引用
useState useReducer 扱えるstateのtype 数値、文字列、論理値 オブジェクト、配列 関連するstateの取り扱い ☓ 複数を同時に取り扱うことが出来る ローカルorグローバル ローカル グローバル useContext()と一緒に取り扱う
小さなコンポーネントならstateかな・・・大きくなる事を考慮するとuseReducerだと思う
useReducerのサンプル1
単純なカウンターになります
useReducerのサンプル1の解説その1
アクションのタイプを指定しています。exportしているのは他のコンポーネントで使う事を考慮しているからです。普通はこの部分だけ別ファイルにするみたい
export const ACTIONS = { INCREMENT: "increment", DECREMENT: "decrement", RESET: "reset" };
useReducerのサンプル1の解説その2
const [state, dispatch] = useReducer(reducer, { count: 0 });
useReducerで状態管理できるようにしています
function increment() { console.log("increment"); dispatch({ type: ACTIONS.INCREMENT }); }
プラスのボタンを押されて時にincrementの関数が実行されてその中にある dispatchという関数が起動されています
function reducer(state, action) { console.log("reducer function"); console.log(`action:${action.type}`); switch (action.type) { case ACTIONS.INCREMENT: return { count: state.count + 1 }; case ACTIONS.DECREMENT: return { count: state.count - 1 }; case ACTIONS.RESET: return { count: 0 }; default: return state; } }
アクションのタイプによって切り替え処理をおこなっています
実際にプラスのボタンを押した時にだされるconsole.logは
になっています
単一のコンポーネントの場合はuseReducerは使わなくても問題ないかと思います
useReducerのサンプル2
Todoリストになります
useReducerのサンプル2の解説その1
サンプル1と同じようにactionのタイプを定義します。
export const ACTIONS = { ADD_TODO: "add-todo", TOGGLE_TODO: "toggle-todo", DELETE_TODO: "delete-todo" };
今回は3つタイプを設定します
useReducerのサンプル2の解説その2
todoを追加するためにフォームを用意しています
<form onSubmit={handleSubmit}> todo: <input type="text" value={name} onChange={(e) => setName(e.target.value)} /> </form>
nameはuseStateで作った変数になります
const [name, setName] = useState("");
定番の入力された内容がnameに保存されるものです。
enterキーが入力されるとonSubmit={handleSubmit}が起動します
function handleSubmit(e) { console.log("handleSubmit"); e.preventDefault(); tododispatch({ type: ACTIONS.ADD_TODO, payload: { name: name } }); setName(""); }
起動されるといつもの再描画をおさえる
e.preventDefault();
が実行されます。
その後、tododispatchが起動します。 サンプル1と違うのはpaylodという部分が増えました。 タイプ以外の部分もdispatchにわたして処理ができます。
function todoreducer(todostate, action) { console.log("todoreducer"); console.log(`action:${action.type}`); switch (action.type) { case ACTIONS.ADD_TODO: return [...todostate, newTodo(action.payload.name)]; ・ ・ ・ ・ function newTodo(name) { return { id: Date.now(), name: name, complete: false }; }
tododispatchの関数が起動されるといつもどおりタイプで処理をわけます。 Todoリストの追加になるので、
[...todostate, newTodo(action.payload.name)];
今までのTodoリストに新しいTodoリストを追加しています。
useReducerのサンプル2の解説その3
Todoリストはいつもどおり配列(todostate)をグルグル回して表示しています
<div>todo list:</div> <div> {todostate.map((todo) => { return ( <Todo key={todo.id} todo={todo} tododispatch={tododispatch} /> ); })} </div>
単純に表示させるではなく子供のコンポーネントに値を渡しています。
<Todo key={todo.id} todo={todo} tododispatch={tododispatch} />
keyの部分はループで回る時のお決まりです。
あとは子供のコンポーネントにpropsで渡しています。
渡す部分にdispatchも渡しているのが味噌です。
子供コンポーネントでTodoの内容とtoggleとdeleteのボタンを表示しています
<div> <span style={{ color: todo.complete ? "#aaa" : "#000" }}> {todo.name} </span> <button onClick={toggle}>toggle</button> <button onClick={deletedata}>delete</button> </div>
toggleのボタンを押すと関数が起動される
function toggle() { console.log(todo.id); tododispatch({ type: ACTIONS.TOGGLE_TODO, payload: { id: todo.id } }); }
その中に親コンポーネントから渡されたdispatchを起動する事で 親のリストを更新しています
function todoreducer(todostate, action) { switch (action.type) { case ACTIONS.TOGGLE_TODO: return todostate.map((todo) => { if (todo.id === action.payload.id) { return { ...todo, complete: !todo.complete }; } return todo; }); default: return todostate; } }
感想
React hooksが好きな所が理解できてよかった。Reduxが苦手なのでこの機能ができて本当に感謝。
人様のブログの記事はなんとなくの理解はできるけど、最終的な理解はcodesandboxのサイトで打ち込んだ時かな・・・
参考URL
https://blog.webdevsimplified.com/2020-06/use-reducer/
https://www.to-r.net/media/react-tutorial-hooks-usereducer/
https://qiita.com/seira/items/2fbad56e84bda885c84c