apeescape2.com
  • メイン
  • 計画と予測
  • Kpiと分析
  • 設計プロセス
  • プロジェクト管理
バックエンド

Reduxを使用したJavaScriptの不変性

リッチで複雑なJavaScriptアプリケーションの成長を続けるエコシステムでは、現在のユーザー、読み込まれた投稿のリストなど、これまで以上に多くの状態を管理する必要があります。

イベントの履歴を必要とするデータのセットは、ステートフルと見なすことができます。状態の管理は困難でエラーが発生しやすい場合がありますが、(可変ではなく)不変のデータと特定のサポートテクノロジー(この記事の目的ではRedux)を使用すると非常に役立ちます。

不変データには、作成後に変更できないという制限がありますが、特に参照と値の同等性において多くの利点があり、データの頻繁な比較(更新が必要かどうかの確認)に依存するアプリケーションを大幅に高速化できます。 、 例えば)。



不変の状態を使用すると、データを再帰的に比較する必要なしに、状態が変化したかどうかをすばやく判断できるコードを記述できます。これは通常、はるかに高速です。

この記事では、アクションクリエーター、純粋関数、構成されたレデューサー、Redux-sagaとRedux Thunkを使用した不純なアクション、そして最後にReactを使用したReduxの使用を通じて状態を管理する場合のReduxの実用的なアプリケーションについて説明します。そうは言っても、MobX、Relay、Fluxベースのライブラリなど、Reduxに代わるものはたくさんあります。

なぜReduxなのか?

ReduxをMobX、Relay、その他のほとんどのFluxベースの実装など、他のほとんどの状態コンテナーから分離する重要な側面は、Reduxには、にディスパッチされる「アクション」(プレーンJavaScriptオブジェクト)を介してのみ変更できる単一の状態があることです。 Reduxストア。他のほとんどのデータストアには、Reactコンポーネント自体に含まれる状態があり、複数のストアを持つことや、可変状態を使用することができます。

これにより、ストアのレデューサー(不変データを操作する純粋関数)が実行され、状態が更新される可能性があります。このプロセスは、より理解しやすく、より決定論的な一方向のデータフローを実施します。

Reduxフロー。

Reduxレデューサーは不変データを操作する純粋関数であるため、同じ入力に対して常に同じ出力を生成し、テストを容易にします。レデューサーの例を次に示します。

import Immutable from 'seamless-immutable' const initialState = Immutable([]) // create immutable array via seamless-immutable /** * a reducer takes a state (the current state) and an action object (a plain JavaScript object that was dispatched via dispatch(..) and potentially returns a new state. */ function addUserReducer(state = initialState, action) { if (action.type === 'USERS_ADD') { return state.concat(action.payload) } return state // note that a reducer MUST return a value } // somewhere else... store.dispatch({ type: 'USERS_ADD', payload: user }) // dispatch an action that causes the reducer to execute and add the user

純粋関数を扱うことで、Reduxは、次のような、一般的に変異状態では簡単に実行できない多くのユースケースを簡単にサポートできます。

  • タイムトラベル(前の状態に戻る)
  • ロギング(ストアで変異を引き起こした原因を特定するために、すべてのアクションを追跡します)
  • コラボレーション環境(アクションがプレーンなJavaScriptオブジェクトであり、シリアル化、ネットワーク経由での送信、別のマシンでの再生が可能なGoogleDocsなど)
  • 簡単なバグレポート(ディスパッチされたアクションのリストを送信し、それらを再生してまったく同じ状態を取得するだけです)
  • 最適化されたレンダリング(少なくとも、Reactなどの状態の関数として仮想DOMをレンダリングするフレームワークでは、不変性のため、オブジェクトを再帰的に比較するのではなく、参照を比較することで何かが変更されたかどうかを簡単に判断できます)
  • 純粋関数は簡単に単体テストできるため、レデューサーを簡単にテストできます

アクションクリエーター

Reduxのアクションクリエーターは、コードをクリーンでテスト可能な状態に保つのに役立ちます。 Reduxの「アクション」は、発生するはずのミューテーションを記述するプレーンなJavaScriptオブジェクトにすぎないことに注意してください。そうは言っても、同じオブジェクトを何度も書き出すことは繰り返しであり、エラーが発生しやすくなります。

諮問委員会の形成方法

Reduxのアクションクリエーターは、ミューテーションを説明するプレーンなJavaScriptオブジェクトを返す単なるヘルパー関数です。これにより、繰り返しのコードを減らし、すべてのアクションを1か所にまとめることができます。

export function usersFetched(users) { return { type: 'USERS_FETCHED', payload: users, } } export function usersFetchFailed(err) { return { type: 'USERS_FETCH_FAILED', payload: err, } } // reducer somewhere else... const initialState = Immutable([]) // create immutable array via seamless-immutable /** * a reducer takes a state (the current state) and an action object (a plain JavaScript object that was dispatched via dispatch(..) and potentially returns a new state. */ function usersFetchedReducer(state = initialState, action) { if (action.type === 'USERS_FETCHED') { return Immutable(action.payload) } return state // note that a reducer MUST return a value }

不変ライブラリでのReduxの使用

レデューサーとアクションの性質上、不変性ヘルパーライブラリがなくても簡単にテストできますが、オブジェクトの変更から保護するものは何もありません。つまり、すべてのレデューサーのテストは特に堅牢である必要があります。

あなたを保護するためのライブラリなしで遭遇する問題の次のコード例を考えてみてください。

const initialState = [] function addUserReducer(state = initialState, action) { if (action.type === 'USERS_ADD') { state.push(action.payload) // NOTE: mutating action!! return state } return state // note that a reducer MUST return a value }

このコード例では、前の状態が現在の状態と同じになるため、タイムトラベルが中断されます。状態への参照が変更されていないため、純粋コンポーネントが更新(または再レンダリング)されない可能性があります。含むが変更され、突然変異を推論するのははるかに困難です。

不変性ライブラリがないと、Reduxが提供するすべての利点が失われます。したがって、特に複数の手がコードに触れる大規模なチームで作業する場合は、immutable.jsやシームレス不変などの不変性ヘルパーライブラリを使用することを強くお勧めします。

使用するライブラリに関係なく、Reduxは同じように動作します。両方の長所と短所を比較して、ユースケースに最も適した方を選択できるようにしましょう。

Immutable.js

Immutable.jsは、Facebookによって構築されたライブラリであり、マップ、リスト、セット、シーケンスなどのデータ構造をより機能的なスタイルで取り入れています。不変の永続データ構造のライブラリは、異なる状態間で可能な限り最小限のコピーを実行します。

長所:

  • 構造共有
  • 更新でより効率的に
  • よりメモリ効率が高い
  • 更新を管理するための一連のヘルパーメソッドがあります

短所:

  • 既存のJSライブラリ(lodash、ramdaなど)とシームレスに連携しません
  • 特に水和/脱水およびレンダリング中に、(toJS / fromJS)との間の変換が必要です

シームレス-不変

シームレス不変は、ES5までの下位互換性がある不変データ用のライブラリです。

次のうち、html5、javascript、およびcssに精通しているのはどれですか?

これは、defineProperty(..)などのES5プロパティ定義関数に基づいていますオブジェクトのミューテーションを無効にします。そのため、lodashやRamdaなどの既存のライブラリと完全に互換性があります。また、本番ビルドで無効にすることもでき、パフォーマンスが大幅に向上する可能性があります。

長所:

  • 既存のJSライブラリ(lodash、ramdaなど)とシームレスに連携します
  • 変換をサポートするために追加のコードは必要ありません
  • 実稼働ビルドでチェックを無効にして、パフォーマンスを向上させることができます

短所:

  • 構造的な共有なし-オブジェクト/配列は浅くコピーされ、大きなデータセットの場合は遅くなります
  • メモリ効率が悪い

Reduxと複数のレデューサー

Reduxのもう1つの便利な機能は、レデューサーを一緒に構成する機能です。これにより、はるかに複雑なアプリケーションを作成でき、かなりのサイズのアプリケーションでは、必然的に複数のタイプの状態(現在のユーザー、読み込まれた投稿のリスト、等)。 Reduxは、関数combineReducersを自然に提供することにより、このユースケースをサポート(および推奨)します。

import { combineReducers } from 'redux' import currentUserReducer from './currentUserReducer' import postsListReducer from './postsListReducer' export default combineReducers({ currentUser: currentUserReducer, postsList: postsListReducer, })

上記のコードを使用すると、currentUserに依存するコンポーネントを作成できます。およびpostsListに依存する別のコンポーネント。これにより、単一のコンポーネントがツリーのブランチに関係するものにのみサブスクライブするため、パフォーマンスも向上します。

Reduxの不純なアクション

デフォルトでは、プレーンJavaScriptオブジェクトのみをReduxにディスパッチできます。ただし、ミドルウェアを使用すると、Reduxは、現在時刻の取得、ネットワークリクエストの実行、ファイルのディスクへの書き込みなどの不純なアクションをサポートできます。

「ミドルウェア」は、ディスパッチされているアクションを傍受できる機能に使用される用語です。インターセプトされると、他のフレームワーク(Express.jsなど)のミドルウェアと同様に、アクションの変換や非同期アクションのディスパッチなどを実行できます。

2つの非常に一般的なミドルウェアライブラリは、ReduxThunkとRedux-sagaです。 Redux Thunkは命令型で書かれていますが、Redux-sagaは機能的なスタイルで書かれています。両方を比較してみましょう。

1099 vsw2時給計算機

Redux Thunk

Redux Thunkは、他のチェーン可能な関数を返す関数であるサンクを使用して、Redux内の不純なアクションをサポートします。 Redux-Thunkを使用するには、最初にReduxThunkミドルウェアをストアにマウントする必要があります。

import { createStore, applyMiddleware } from 'redux' import thunk from 'redux-thunk' const store = createStore( myRootReducer, applyMiddleware(thunk), // here, we apply the thunk middleware to R )

これで、サンクをReduxストアにディスパッチすることで、不純なアクション(API呼び出しの実行など)を実行できます。

store.dispatch( dispatch => { return api.fetchUsers() .then(users => dispatch(usersFetched(users)) // usersFetched is a function that returns a plain JavaScript object (Action) .catch(err => dispatch(usersFetchError(err)) // same with usersFetchError } )

サンクを使用すると、コードのテストが難しくなり、コードフローによる推論が難しくなる可能性があることに注意してください。

Redux-saga

Redux-sagaは、 ES6(ES2015) ジェネレーターと呼ばれる機能と関数型/純粋ヘルパーのライブラリ。ジェネレーターの優れている点は、ジェネレーターを再開および一時停止できることです。また、APIコントラクトにより、ジェネレーターのテストが非常に簡単になります。

sagasを使用して以前のサンクメソッドの読みやすさとテスト容易性をどのように改善できるか見てみましょう!

まず、Redux-sagaミドルウェアをストアにマウントしましょう。

import { createStore, applyMiddleware } from 'redux' import createSagaMiddleware from 'redux-saga' import rootReducer from './rootReducer' import rootSaga from './rootSaga' // create the saga middleware const sagaMiddleware = createSagaMiddleware() // mount the middleware to the store const store = createStore( rootReducer, applyMiddleware(sagaMiddleware), ) // run our saga! sagaMiddleware.run(rootSaga)

run(..)に注意してください関数の実行を開始するには、sagaで関数を呼び出す必要があります。

それでは、サガを作成しましょう。

import { call, put, takeEvery } from 'redux-saga/effects' // these are saga effects we'll use export function *fetchUsers(action) { try { const users = yield call(api.fetchUsers) yield put(usersFetched(users)) } catch (err) { yield put(usersFetchFailed(err)) } } export default function *rootSaga() { yield takeEvery('USERS_FETCH', fetchUsers) }

ユーザーリストとrootSagaをフェッチする2つのジェネレーター関数を定義しました。 api.fetchUsersを呼び出さなかったことに注意してください直接ですが、代わりに呼び出しオブジェクトでそれを生成しました。これは、Redux-sagaが呼び出しオブジェクトをインターセプトし、そこに含まれる関数を実行して純粋な環境を作成するためです(ジェネレーターに関する限り)。

rootSaga takeEvery,という関数への単一の呼び出しを生成しますUSERS_FETCHのタイプでディスパッチされたすべてのアクションを実行しますそしてfetchUsersを呼び出しますそれがとった行動を伴う佐賀。ご覧のとおり、これによりReduxの非常に予測可能な副作用モデルが作成され、テストが簡単になります。

Sagasのテスト

ジェネレーターによってサガのテストが簡単になる方法を見てみましょう。使用します モカ この部分では、単体テストを実行し、 チャイ アサーション用。

sagasはプレーンなJavaScriptオブジェクトを生成し、ジェネレーター内で実行されるため、モックなしで正しい動作を実行することを簡単にテストできます。 callに注意してください、take 、putなどは、Redux-sagaミドルウェアによってインターセプトされる単なるJavaScriptオブジェクトです。

import { take, call } from 'redux-saga/effects' import { expect } from 'chai' import { rootSaga, fetchUsers } from '../rootSaga' describe('saga unit test', () => { it('should take every USERS_FETCH action', () => { const gen = rootSaga() // create our generator iterable expect(gen.next().value).to.be.eql(take('USERS_FETCH')) // assert the yield block does have the expected value expect(gen.next().done).to.be.equal(false) // assert that the generator loops infinitely }) it('should fetch the users if successful', () => { const gen = fetchUsers() expect(gen.next().value).to.be.eql(call(api.fetchUsers)) // expect that the call effect was yielded const users = [ user1, user2 ] // some mock response expect(gen.next(users).value).to.be.eql(put(usersFetched(users)) }) it('should fail if API fails', () => { const gen = fetchUsers() expect(gen.next().value).to.be.eql(call(api.fetchUsers)) // expect that the call effect was yielded const err = { message: 'authentication failed' } // some mock error expect(gen.throw(err).value).to.be.eql(put(usersFetchFailed(err)) }) })

Reactでの作業

Reduxは特定のコンパニオンライブラリに関連付けられていませんが、特にうまく機能します React.js Reactコンポーネントは、状態を入力として受け取り、仮想DOMを出力として生成する純粋関数であるためです。

React-Reduxは、ReactとReduxのヘルパーライブラリであり、2つを接続するハードワークのほとんどを排除します。 React-Reduxを最も効果的に使用するために、プレゼンテーションコンポーネントとコンテナコンポーネントの概念を見ていきましょう。

プレゼンテーションコンポーネントは、レンダリングする小道具のみに応じて、物事が視覚的にどのように見えるかを記述します。小道具からコールバックを呼び出してアクションをディスパッチします。それらは手書きで完全に純粋であり、Reduxのような状態管理システムに結び付けられていません。

一方、コンテナコンポーネントは、物事がどのように機能するかを記述し、Reduxを認識し、Reduxアクションを直接ディスパッチしてミューテーションを実行し、通常はReact-Reduxによって生成されます。それらはしばしばプレゼンテーションコンポーネントとペアになって、その小道具を提供します。

Reduxのプレゼンテーションコンポーネントとコンテナコンポーネント。

ここに示されている知覚組織の原理

プレゼンテーションコンポーネントを作成し、React-Reduxを介してReduxに接続しましょう。

const HelloWorld = ({ count, onButtonClicked }) => ( Hello! You've clicked the button {count} times! Click me ) HelloWorld.propTypes = { count: PropTypes.number.isRequired, onButtonClicked: PropTypes.func.isRequired, }

これは、機能するためにその小道具に完全に依存する「ダム」コンポーネントであることに注意してください。これは素晴らしいです、なぜならそれは テストが簡単で構成が簡単なReactコンポーネント 。このコンポーネントをReduxに接続する方法を見てみましょう。最初に、高階コンポーネントとは何かについて説明します。

高階コンポーネント

React-Reduxはconnect( .. )と呼ばれるヘルパー関数を提供しますこれは、Reduxを認識する「ダム」Reactコンポーネントから高次のコンポーネントを作成します。

Reactは、コンポーネントを他のコンポーネントでラップするときの構成を通じて、拡張性と再利用性を強調します。これらのコンポーネントをラップすると、動作が変更されたり、新しい機能が追加されたりする可能性があります。 Reduxを認識しているプレゼンテーションコンポーネント(コンテナコンポーネント)から高次コンポーネントを作成する方法を見てみましょう。

方法は次のとおりです。

import { connect } from 'react-redux' const mapStateToProps = state => { // state is the state of our store // return the props that we want to use for our component return { count: state.count, } } const mapDispatchToProps = dispatch => { // dispatch is our store dispatch function // return the props that we want to use for our component return { onButtonClicked: () => { dispatch({ type: 'BUTTON_CLICKED' }) }, } } // create our enhancer function const enhancer = connect(mapStateToProps, mapDispatchToProps) // wrap our 'dumb' component with the enhancer const HelloWorldContainer = enhancer(HelloWorld) // and finally we export it export default HelloWorldContainer

2つの関数mapStateToPropsを定義したことに注意してください。およびmapDispatchToProps 。

mapStateToProps Redux状態から計算されたオブジェクトを返す(状態:オブジェクト)の純粋関数です。このオブジェクトは、ラップされたコンポーネントに渡された小道具とマージされます。これは、コンポーネントの小道具にマージするRedux状態の一部を選択するため、セレクターとも呼ばれます。

mapDispatchToPropsも純粋関数ですが、Reduxディスパッチ関数から計算されたオブジェクトを返す(dispatch:(Action)=> void)の1つです。このオブジェクトは、同様に、ラップされたコンポーネントに渡された小道具とマージされます。

コンテナコンポーネントを使用するには、Providerを使用する必要があります。 React-Reduxのコンポーネントは、コンテナコンポーネントに使用するストアを指示します。

import { Provider } from 'react-redux' import { render } from 'react-dom' import store from './store' // where ever your Redux store resides import HelloWorld from './HelloWorld' render( ( ), document.getElementById('container') )

Providerコンポーネントは、Reduxストアにサブスクライブするすべての子コンポーネントにストアを伝播し、すべてを1つの場所に保持し、エラーや変更のポイントを減らします。

Reduxでコードの信頼性を構築する

Redux、その多数のサポートライブラリ、およびReact.jsとのフレームワーク接続に関するこの新たに発見された知識を使用すると、状態制御を通じてアプリケーションのミューテーションの数を簡単に制限できます。強力な状態制御により、より速く移動し、より自信を持って堅実なコードベースを作成できます。

予算にやさしいデータマイニングのガイド

バックエンド

予算にやさしいデータマイニングのガイド
モバイルWebアプリケーションの開発:いつ、なぜ、そしてどのように

モバイルWebアプリケーションの開発:いつ、なぜ、そしてどのように

Webフロントエンド

人気の投稿
Vue3でのオンデマンドの反応性
Vue3でのオンデマンドの反応性
VanillaJSでのReactとJSXのエミュレート
VanillaJSでのReactとJSXのエミュレート
カスタムMagento2ウィジェットを作成する方法
カスタムMagento2ウィジェットを作成する方法
不和ボットの作り方:概要とチュートリアル
不和ボットの作り方:概要とチュートリアル
JavaScriptデザインパターンの包括的なガイド
JavaScriptデザインパターンの包括的なガイド
 
Google BigQueryを使用する意味があるのはいつですか?
Google BigQueryを使用する意味があるのはいつですか?
Pythonビデオストリーミングでポルノを20倍効率化した方法
Pythonビデオストリーミングでポルノを20倍効率化した方法
ディストレストM&A:バーゲン購入の機会の評価
ディストレストM&A:バーゲン購入の機会の評価
スニペットブラウジングパターンのAndroid開発者ガイド
スニペットブラウジングパターンのAndroid開発者ガイド
究極のUXフック– UXにおける予測的、説得力のある、感情的なデザイン
究極のUXフック– UXにおける予測的、説得力のある、感情的なデザイン
人気の投稿
  • クラスangularjsで要素を取得
  • データベースのパフォーマンスを向上させる方法
  • cvvでクレジットカード番号をハックする
  • ラズベリーパイオープンポート8080
  • 機械学習を定義し、いくつかの例を挙げます
カテゴリー
プロジェクト管理 ライフスタイル 技術 設計プロセス 製品の担当者とチーム モバイルデザイン 収益性と効率性 リモートの台頭 収益と成長 Uiデザイン

© 2021 | 全著作権所有

apeescape2.com