Redux

Reduxのライフサイクルはピュアでシンクロナス

0
0

「ライフサイクル」という言葉は、

 

reduxのデータフローとオペレーションの文脈で使われています。

 

たった3年で、reduxはフロントエンド開発者の間で、

 

状態管理ライブラリとしてかなり人気のある選択肢になりました。

 

Reduxは、コードベースが小さくてシンプルであるだけでなく、

 

非常に複雑な状況を簡単に処理することができます。

 

今回の記事では、ミドルウェアがReduxのライフサイクルやデータフローにどのような影響を与えるかを探っていきます。

 

Reduxの基本的なライフサイクル

 

内部的には、reduxは同期データフローでのみ動作し、他のソリューションのように多くの「動く部品」や「魔法のメソッド」は備えていません。

 

最も基本的な redux のセットアップは、たった 2 つのコンポーネントで構成された状態更新のシングルループと考えることができます。

 

  • state treeと
  • reducer functionです

 

アクションは、イテレーションのために状態をどのように更新するかを把握するために必要なアクセサリーオブジェクトです。

 

reducer関数は、前の状態とディスパッチされたアクションを引数にとり、次の状態を返します。

 

変更の必要がない場合は、前の状態をそのまま返します。それ以外の場合は、新しい状態を作成して返します。

 

レデューサの分解

ステートツリーを複数のスライスに分割し、各ステートスライスに対して個別のレデューサーを書くのが一般的な方法です。

 

アクションは複数のステートスライスに関係している場合もあれば、そうでない場合もあります。

 

 

レデューサの結合

複数の独立したレデューサを1つのレデューサ(「ルート」レデューサと呼ばれることが多い)に戻し、

 

そのレデューサを使ってreduxストアを作成することができます。

 

import { combineReducers } from 'redux'

 

import fooReducer from './foo'
import barReducer from './bar'
​
const rootReducer = combineReducers({
  foo: fooReducer,
  bar: barReducer,
})

 

 

アクションがディスパッチされると、ルートのreducerがそれを受け取り、

 

すべてのreducerに同じアクションオブジェクトを渡して、各reducerが独立してアクションに反応できるようにします。

 

ただし、reduxはステートツリー全体を各reducerに渡すのではなく、reducerの排他的なステートスライスのみを渡す。

 

そして、各リデューサからの戻り値は、完全なステートツリーにマージされて戻ってきます。

 

これが基本的なreduxのアーキテクチャであり、最も一般的なものでもあります。

 

綿密な計画を立て、アプリの状態をスマートに分割すれば、ほとんどの小規模アプリは複雑さを増すことなくこのアーキテクチャで動作します。

 

ステートに依存するリデューサ

 

ステートツリーが複雑になってくると、異なるリデューサ間でステートを共有しなければならない状況に陥ることがあります。

 

手っ取り早い方法は、それらのレデューサを1つにまとめることです。しかし、そうすると大抵の場合、拡張性のない太ったレデューサができてしまいます。

 

レデューサを別々にしたい場合は、現在の状態のスライスを共有するのか、それとも更新されたバージョンを共有するのかを把握する必要があります。

 

状態に依存するReducer - 現在の状態

 

reducerは単なる関数なので、好きな数の引数を渡すことができます。

 

あるリデューサが他のリデューサからのステートスライスを必要とする場合、それを第3引数として渡すことができます。

 

 

レデューサの結合

 

reduxに同梱されているcombineReducers()ユーティリティを使って、

 

依存するReducerを組み合わせることはできません。

 

combineReducersのコアなユースケースを超えたら、

 

もっと「カスタム」なreducerのロジックを使うべきです。

 


import foo from './foo'
import bar from './bar'
​
function rootReducer(state, action) {
  const fooState = foo(state, action)
  const barState = bar(state, action, state.foo)
  return {
    foo: fooState,
    bar: barState,
  }
}

  

状態に依存するレデューサ - 更新された状態

 

一般的に、レデューサーが他のレデューサーのステートに依存する場合、

 

更新されたステートが必要となりますが、

 

個人的には、古い状態が要求される状況に遭遇したことは、まあありません。

 

 

 

レデューサの組み合わせ

 

コードの見た目は、前のセクションとほとんど変わりません。

 

ほんの少しだけ変更を加えています。

 

古い状態を state.foo に渡すのではなく、

 

更新された状態を fooState に渡しています。

 

import foo from './foo'
import bar from './bar'
​
function rootReducer(state, action) {
  const fooState = foo(state, action)
  const barState = bar(state, action, fooState)


return {
    foo: fooState,
    bar: barState,
  }
}

他のレデューサの更新された状態に依存するということは、

 

レデューサの呼び出しには垂直方向の階層が存在することを意味します。

 

これは些細なケースではあまり意味がありませんが、

 

このアイデアはリデューサを連鎖させる可能性を示唆していまして、

 

レデューサを連鎖させると、コードの複雑さが指数関数的に増大します。

 

注:「シェアード・スライス・アップデート」の問題に対する別の選択肢は、単純にアクションに多くのデータを入れることです。これについては、こちらをご覧ください。

 

全てをまとめる

reducerのレイアウトや組み合わせのパターンをいくつか見てきましたが、

 

今度はそれらがどのように組み合わされるのかを見てみましょう。

 

観察すべき重要な点は以下の通りです。

 

  • 各Reducerは、第1引数として排他的なスライスの状態を受け取る。
  • 各Reducerは、同じアクションオブジェクトを第2引数として受け取る。
  • 依存するリデューサは、完全な状態または他のリデューサの状態スライ
  • スを後続の引数として受け取る。
  • 更新されたすべてのステート スライスは、単一のステート ツリーに統合される。
  • 依存関係にあるリデューサは、呼び出しチェーンを形成することができる
  • Reduxはreducerを使って状態操作を行う

 

今日の記事は以上になります。

 

この記事は下記の英語の記事を和訳して要点だけ抜いたものです、詳し区全文読みたい方は本家へどうぞ。

 

Pocket
LinkedIn にシェア

りょすけ
毎月3万人以上のプログラマーが訪れるoff.tokyoで、プログラマーさん向けのアンケートを実施しています、アンケートに無料回答すると、他の人の投票も見ることが出来ます!

学びたいプログラミング言語は何ですか?

 vaaaval@gmail.com



  • この記事を書いた人
  • 最新記事

藤沢瞭介(Ryosuke Hujisawa)

りょすけと申します。18歳からプログラミングをはじめ、今はフロントエンドでReactを書いたり、AIの勉強を頑張っています。off.tokyoでは、ハイテクやガジェット、それからプログラミングに関する情報まで、エンジニアに役立つ情報を日々発信しています!

-Redux

Copyright© off.tokyo , 2021 All Rights Reserved Powered by AFFINGER5.