はじめに
こんばんは。続いてReact勉強してます。
今回から、勉強するときのメンツが集まりやすくなったから、頻繁にできそうかも。
わくわく。
React入門 React・Reduxの導入からサーバサイドレンダリングによるUXの向上まで (NEXT ONE)
- 作者: 穴井宏幸,石井直矢,柴田和祈,三宮肇
- 出版社/メーカー: 翔泳社
- 発売日: 2018/02/19
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
やってみた
目次
今回やったのはこちら
- ライフサイクル
- 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導入ってやってみたいなーと思う今日このごろでした。
お疲れ様でしたー。