LoginSignup
1
2

More than 3 years have passed since last update.

Udemy で React Hooks を学んだ時のメモ useReducer & useContext編

Posted at

useState,useEffect編に引き続き今回はUseReducer

ショック!!!Evernoteが消えた!!!

いきなり本文とまったく関係ない話題。
Evernoteにメモをとっていたのだがそのノートが消えてしまった。サポートに問い合わせてはいるがもう戻って来ないのだろう・・・
俺の正月を返せ・・・甥っ子姪っ子ともっと遊びたかったのに・・・

次回から直接Qiitaに下書きしようかな。
ので今回からは更に雑なメモとなっている。

今回お世話になったコース

React Hooks 入門 - Hooksと Redux を組み合わせて最新のフロントエンド状態管理手法を習得しよう!

ご存知の方も多いと思うがUdemyは頻繁にセールを行っている為お安く購入する事が可能。
セール時に購入すれば手を動かすチュートリアル的な存在としては激安だと思うので興味のある方にはお勧めしたい。
余談ではあるが今回受講しているコースは、毎動画毎にdiffを確認してくれているのでそこを見返すだけでも良い復讐になった。

状態遷移とは何か?

まずコースではwikipediaの説明を使って状態遷移とは何かを改めて学んだ。

状態遷移表の例

UseReducer

Reduxを使用している方には説明不要であろう。
「reducer」とは、2つの値を取り、1つの値を返す関数を意味する。

Reducerの作成

const events = (state = [], action) => {
  switch (action.type) {
    case CREATE_EVENT:
      const event = { title: action.title, body: action.body };
      const length = state.length;
      const id = length === 0 ? 1 : state[length - 1].id + 1;
      return [...state, { id, ...event }];
    case DELETE_EVENT:
      return state.filter(event => event.id !== action.id);
    case DELETE_ALL_EVENTS:
      return []; // 削除した時は空配列を返す
    default:
      return state; // 何もしたくない
  }
};
export default events;

引数はstateとactionの両方を受け取る。
stateは未定義のこともあるので初期化が必要。

Reducerを使っていく

const [state, dispatch] = useReducer(reducer, initialArg, init);

import React, { useReducer, useState } from "react";

import "bootstrap/dist/css/bootstrap.min.css";

import reducer from "../reducers";

const App = () => {
  const [state, dispatch] = useReducer(reducer, []);
  const [title, setTitle] = useState("");
  const [body, setBody] = useState("");
  const addEvent = e => {
    e.preventDefault();

    dispatch({
      type: "CREATE_EVENT",
      title,
      body
    });

    setTitle("");
    setBody("");
  };

  return (
    <div className="container-fluid">
      <h4>イベント作成フォーム</h4>
      <form>
        <div className="form-group">
          <label htmlFor="formEventTitle">タイトル</label>
          <input
            className="form-control"
            id="formEventTitle"
            value={title}
            onChange={e => setTitle(e.target.value)}
          />
        </div>
        <div className="form-group">
          <label htmlFor="formEventBody">ボディー</label>
          <textarea
            className="form-control"
            id="formEventBody"
            value={body}
            onChange={e => setBody(e.target.value)}
          />
        </div>
        <button className="btn btn-primary" onClick={addEvent}>
          イベントを作成する
        </button>
        <button className="btn btn-danger">全てのイベントを削除する</button>
      </form>
      <h4>イベント一覧</h4>
      <table className="table table-hover">
        <thead>
          <tr>
            <th>ID</th>
            <th>タイトル</th>
            <th>ボディー</th>
            <th></th>
          </tr>
        </thead>
        <tbody></tbody>
      </table>
    </div>
  );
};

export default App;

UseReducerの欠点

現状2つ以上のReducerを一つのReducerに統合する事が出来ない。
そういう時はReduxのCombineReducersを使う。

useContext

Prop Drilling問題

・Reactでは、配下のコンポーネントにデータを渡すための手段としてpropsという機能が提供されているが、そのpropsが親→子→孫を言った感じでバケツリレーを行うような処理が必要になってしまう事。
・小規模であれば問題はないが大規模になると苦行となる。
・さらにそのコンポーネントに直接必要ではないpropsもリレーする為だけに記載をしなくてはいけなくなり、コードの見通しが悪くなる。

Reduxではだめなの?

・当然その解決策としてreduxを使えば配下コンポーネントに伝搬させたい状態をProviderという特殊なコンポーネントに渡してあげる事で任意の配下コンポーネントでその情報を参照することが可能。
・ただしreduxは導入が面倒で難しい。例えばただデータを共有したいだけなのにデータを参照したい全てのコンポーネントにmapStateToProps、mapDispatchToProps、connectの記載が必要になってしまう
・小規模の場合は状態管理をreduxに頼らなくてもreact純正の機能で対応出来る。それがReact Context。

React Context

React Context自体はhooksの機能ではないが useContextを使用する際に大前提となるので先に共有

コンテキスト
https://ja.reactjs.org/docs/context.html

Document
https://ja.reactjs.org/docs/context.html#reactcreatecontext

import { createContext } from 'react'

const AppContext = createContext()

export default AppContext

Hooks以前のやり方

Context.Provider

<MyContext.Provider value={/* 何らかの値 */}>

既存のdomをproviderでラップする。
そしてProviderにはvalueという特別なpropsがある。valueになんらかの値を渡してあげればOK。

<AppContext.Provider value={'Hello, I am a Provider.'}>
      <div className="container-fluid">
        <EventForm state={state} dispatch={dispatch} />
        <Events state={state} dispatch={dispatch} />
      </div>
 </AppContext.Provider>

Context.Consumer

<MyContext.Consumer>
  {value => /* コンテクストの値に基づいて何かをレンダーします */}
</MyContext.Consumer>

子に関数を記述することが出来る。
valueとはproviderで設定したvalue、この場合は{'Hello, I am a Provider.'}を受け取れる。
一歩行にデータが流れる。

HooksのuseContextを使うと

import React, { useContext } from 'react'
import AppContext from '../contexts/AppContext'

const Events = ({ state, dispatch }) => {
  const value = useContext(AppContext)

こう言った感じで受け取れる。こうする事によって共有したいものをpropsを介さずに、つまりバケツリレーのような事を行わずに受け取る事が可能になる。
これがuseContext。

メリットとしてはpropsを中継ぎしているようなコンポーネントのコードの記述量が減る事によってすっきりする。
ただ感じた事は後々コードを読む時にproviderの値がどこで使われているかを追いづらいかもしれない思った。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2