もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

React入門を読み始めた(パート3)

はじめに

こんばんは。続いてReact勉強してます。

前回はこちら

今回から、勉強するときのメンツが集まりやすくなったから、頻繁にできそうかも。

わくわく。

React入門 React・Reduxの導入からサーバサイドレンダリングによるUXの向上まで (NEXT ONE)

React入門 React・Reduxの導入からサーバサイドレンダリングによるUXの向上まで (NEXT ONE)

やってみた

目次

今回やったのはこちら

  1. ライフサイクル
  2. Reduxでアプリケーションの状態を管理しよう(途中まで)

ライフサイクル

ライフサイクルメソッドは前回で少し説明したとおり、ClassComponentに備わっている独自メソッド。

大きく分けて3種類で、その3種類の中でもさらに細かく分かれている。

  • マウントに関するライフサイクルメソッド

    • componentWillMount
    • componentDidMount
    • componentWillUnmount
  • データのアップデートに関するライフサイクルメソッド

    • componentWillReceiveProps
    • shouldComponentUpdate
    • componentWillUpdate
    • componentDidUpdate
  • エラーハンドリングに関するライフサイクルメソッド

    • componentDidCatch

1. マウントに関するライフサイクルメソッド

componentWillMount

コンポーネントがマウントされる直前に実行されるメソッド。

ちなみに「マウントされた状態」というのは、Reactコンポーネントのrender()が初めて呼ばれたときが、マウントされた状態ということのようだ。

つまりこの componentWillMount は render()が初めて呼ばれる直前に呼ばれるメソッドということ。

使いみちはどんな感じなんだろう。

constructor()が終わって、render()が走る前にやりたいことがあまり思い浮かばない。

componentDidMount

コンポーネントがマウントされた直後に実行されるメソッド。

マウントされたあと(render()が終わったあと)なので、実際のDOMにアクセスできるから、イベントリスナーを登録したりするのに、最適なタイミングみたい。

componentWillUnmount

コンポーネントがアンマウントされる直前に実行されるメソッド。

「アンマウントされた状態」というのは、DOM上から対象のコンポーネントがなくなったときに、アンマウントされた状態ということのようだ。

DOMがなくなったときに実行するので、destructor()的なイメージかと思う。

なので、もろもろの掃除をしてあげるのが良さそう。

2. データのアップデートに関するライフサイクルメソッド

componentWillReceiveProps

propsを受け取る直前に呼ばれるメソッド。

このメソッドの引数には、新たに受け取る予定のpropsがある。

componentWillReceiveProps(nextProps)
{
}

このメソッドの引数の nextPropsは新たに受け取るpropsが入っていて、this.propsには、以前に受け取ったprops(更新前のprops)が入っている。

なので、新たに受け取ったpropsと、受け取る前(更新前)のpropsを比べる処理などがかける...と思う...

shouldComponentUpdate

propsやstateに変更があったときに呼ばれるメソッド。

このメソッドの引数には、新たに受け取る予定のpropsとstateがある。

shouldComponentUpdate(nextProps, nextState)
{
}

このメソッドにはtrue or falseの戻り値があり、trueを返すと、render()を実行し、falseを返すとrender()が実行されなくなる。

前回と同じpropsやstateが送られた場合は、render()を実行する必要がないのでfalseを返して、無駄なrender()を走らせないようにすることができる...らしい。

componentWillUpdate

render()が呼ばれる前に呼ばれるメソッド。

このメソッドの引数には、新たに受け取る予定のpropsとstateがある。

componentWillUpdate(nextProps, nextState)
{
}

2回目以降 のrender()が呼ばれるたびに、その直前に実行される。

先に説明した、componentWillMount() は初めてrender()が実行される直前に呼ばれるので、このメソッドは使いみちが違うっぽい。

componentDidUpdate

render()が呼ばれた直後呼ばれるメソッド。

このメソッドの引数には、以前のpropsとstateがある。

componentDidUpdate(prevProps, prevState)
{
}

2回目以降 のrender()が呼ばれるたびに、その直後に実行される。

コレも同じく、先に説明した、componentDidMount()とはタイミングが違うので、使いみちが違う。

でも前の状態のpropsやstateを持って何をするんだろう??

例えば割引額を表示するコンポーネントだったとして、選んだ割引クーポンごとに、金額の差額を表示するとかかな??

うーん。

本の解説では、DOMへアクセスするタイミングとしてはここが一番とのこと。てことは↑の例も当たりってことかな?

3. エラーハンドリングに関するライフサイクルメソッド

componentDidCatch

コンポーネントでエラーが起こったときに呼ばれるメソッド。

引数には、スタックトレースのerrorと、その他情報が入ったinfoがある。

componentDidCatch(error, info)
{
}

あくまで子コンポーネントでエラーが起こった時に呼ばれるので、つまり、この componentDidCatch を記述したコンポーネントでエラーが発生しても呼ばれない。

Reduxでアプリケーションの状態を管理しよう

前々回くらいでやったReduxで、前回と同じようなTODOアプリケーションを作ろうというスタンス。

ボリュームが多かったので、途中までで止まってる。

今回やったのは、Storeを生成して、ユーザーの行動を思わせるような擬似的なActionを作成し、そのActionを、生成したStoreにわたし、StoreにセットされているReducerが、状態を変化させる

というところまでをやってみた。

コード

Action

Actionを生成するための関数(ActionCreator)を用意する。

const addTask (task) => ({
  type: 'ADD_TASK',
  payload: {
    task
  }
});

Actionの形式には、Flux Standard Action という標準化された形式があるみたい。

↑の形がそう。

なので、Actionの要素も決まっている。

  • payload

    Actionに伴うデータとして利用できる。

  • error

    エラーを表現したい場合にはtrueにしてあげる。それに伴ってpayloadの中身も変化させてあげる。

  • meta

    payloadとは別の情報を含めたい場合に利用できる。

Reducer

Storeにセットする用のReducerを作成する。

const initialStore = {
  tasks: []
};

function addReducer(state = initialStore, action) {
  switch (action.type) {
    case 'ADD_TASK':
      return (
         ...state,
         tasks: state.tasks.concat([action.payload.task]);
      );
    default:
      return state;
  }
}
Store

状態管理を行うStoreを作成する。

import { createStore } from 'redux';

const store = createStore(addReducer);

createStore()で作成したstoreには4つのメソッドが存在している。

  • dispatch

    アクションを発行する。

const store = createStore(addReducer);
store.dispatch(addTask("タスク追加"));
  • subscribe

    ストアの更新が行われた際のコールバックメソッドを設定できる。

function changed() {
  console.log("変更されたよ!");
}
const store = createStore(addReducer);
const unsubscribe = store.subscribe(changed());

subscribe()の引数で受け取った unsubscribe を実行すると、subscribe()で設定したコールバックは呼ばれなくなる。

  • getState

    現在のStateを取得する。

const store = createStore(addReducer);
console.log(store.getState());
  • replaceReducer

    Reducerを切り替える。

const store = createStore(addReducer);
store.replaceReducer(別のReducer);

createStoreによって作成されたStoreにはReducerは一つしかセットできない。

なので、このような切り替えるためのメソッドがあるみたい。

アプリケーションが大規模なほど、Reducerも分割したほうが使いやすいとおもうけど、毎回切り替えるのはダルそう。というかミス起きそう。

今回はそこまで行けなかったので説明できないけど、そんなときのために combineReducer という 複数のReducerを一つにまとめるメソッドがあるみたい。

上記で書いた3つ(action, reducer, store)を組み合わせると以下のようなコードになる。

import { createStore } from 'redux';

const addTask (task) => ({
  type: 'ADD_TASK',
  payload: {
    task
  }
});

const initialStore = {
  tasks: []
};

function addReducer(state = initialStore, action) {
  switch (action.type) {
    case 'ADD_TASK':
      return (
         ...state,
         tasks: state.tasks.concat([action.payload.task]);
      );
    default:
      return state;
  }
}

function changed() {
  console.log("変更されたよ!");
}
    
const store = createStore(addReducer);
store.subscribe(changed());
store.dispatch(addTask('タスク1'));
store.dispatch(addTask('タスク2'));
store.dispatch(addTask('タスク3'));
console.log(store.getState());

=>
変更されたよ!
変更されたよ!
変更されたよ!
[タスク1, タスク2, タスク3]

こんな感じ!!!

おわりに

今回も結構面白い内容でした。

第一章でやったReduxの説明と概念は正直あんまわかってなかったけど、進めながらやると少しだけわかったような気もします。

まだまだ触りだけど。。。w

サイトの一部分だけReact + Redux導入ってやってみたいなーと思う今日このごろでした。

お疲れ様でしたー。