リッチでパワフルなウェブアプリやモバイルアプリの成長するエコシステムでは、現在のユーザー、読み込まれたアイテムのリスト、読み込み状態、エラーなど、管理する状態がますます増えています。 Reduxは、状態をグローバルオブジェクトに保持することにより、この問題を解決する1つの方法です。
Reduxの制限の1つは、すぐに使用できる非同期動作をサポートしていないことです。これに対する1つの解決策は、JavaScriptでのリアクティブプログラミング用の強力なライブラリであるRxJSに基づくredux-observable
です。 RxJSは、Microsoftで開発されたリアクティブプログラミング用のAPIであるReactiveXの実装です。 ReactiveXは、リアクティブパラダイム、関数型プログラミング、オブザーバーパターン、イテレーターパターンの最も強力な機能のいくつかを組み合わせたものです。
このチュートリアルでは、ReduxとReactでの使用法について学習します。また、RxJSを使用したリアクティブプログラミングと、それによって面倒で複雑な非同期作業を非常に簡単にする方法についても説明します。
最後に、RxJSを利用して非同期作業を行うライブラリであるredux-observableを学習し、Reduxとredux-observableを使用してReactNativeでアプリケーションを構築します。
GitHubで説明されているように、Reduxは「JavaScriptアプリの予測可能な状態コンテナー」です。 JavaScriptアプリにグローバルな状態を提供し、状態とアクションをReactコンポーネントから遠ざけます。
Reduxを使用しない一般的なReactアプリケーションでは、プロパティまたはprops
を介してルートノードから子にデータを渡す必要があります。このデータフローは小さなアプリケーションでは管理できますが、アプリケーションが大きくなるにつれて非常に複雑になる可能性があります。 Reduxを使用すると、コンポーネントを互いに独立させることができるため、信頼できる唯一の情報源として使用できます。
ReduxはReactで使用できます react-redux
、ReactコンポーネントがReduxからデータを読み取り、アクションをディスパッチしてReduxの状態を更新するためのバインディングを提供します。
Reduxは、次の3つの簡単な原則として説明できます。
ザ・ 状態 アプリケーション全体の一部が単一のオブジェクトに保存されます。 Reduxのこのオブジェクトは お店 。 Reduxアプリには1つのストアが必要です。
» console.log(store.getState()) « { user: {...}, todos: {...} }
ReactコンポーネントのReduxからデータを読み取るには、 connect
react-redux
からの関数。 connect
4つの引数を取りますが、これらはすべてオプションです。今のところ、mapStateToProps
と呼ばれる最初のものに焦点を当てます。
/* UserTile.js */ import { connect } from 'react-redux'; class UserTile extends React.Component { render() { return { this.props.user.name }
} } function mapStateToProps(state) { return { user: state.user } } export default connect(mapStateToProps)(UserTile)
上記の例では、mapStateToProps
最初の引数としてグローバルRedux状態を受け取り、親コンポーネントから渡された小道具とマージされるオブジェクトを返します。
Reduxの状態はReactコンポーネントでは読み取り専用であり、状態を変更する唯一の方法は、 アクション 。アクションは、状態を変更する意図を表すプレーンオブジェクトです。すべてのアクションオブジェクトにはtype
が必要ですフィールドであり、値は文字列である必要があります。それ以外は、アクションの内容は完全にあなた次第ですが、ほとんどのアプリは フラックス標準アクション フォーマット。アクションの構造を4つのキーのみに制限します。
type
アクションの任意の文字列識別子。すべてのアクションには固有のアクションが必要です。payload
アクションのオプションデータ。いつでも使用でき、アクションに関する情報が含まれています。error
アクションがエラーを表す場合、任意のブールプロパティをtrueに設定します。これは、拒否されたPromise. string
に類似しています。アクションの識別子。すべてのアクションには固有のアクションが必要です。慣例により、error
の場合はtrue
、payload
ですエラーオブジェクトである必要があります。meta
メタは任意のタイプの値にすることができます。これは、ペイロードの一部ではない追加情報を対象としています。アクションの2つの例を次に示します。
store.dispatch({ type: 'GET_USER', payload: '21', }); store.dispatch({ type: 'GET_USER_SUCCESS', payload: { user: { id: '21', name: 'Foo' } } });
グローバルRedux状態は、レデューサーと呼ばれる純粋関数を使用して変更されます。レデューサーは前の状態とアクションを取り、次の状態を返します。レデューサーは、既存の状態オブジェクトを変更する代わりに、新しい状態オブジェクトを作成します。アプリのサイズに応じて、Reduxストアには単一のレデューサーまたは複数のレデューサーを含めることができます。
/* store.js */ import { combineReducers, createStore } from 'redux' function user(state = {}, action) { switch (action.type) { case 'GET_USER_SUCCESS': return action.payload.user default: return state } } function todos(state = [], action) { switch (action.type) { case 'ADD_TODO_SUCCESS': return [ ...state, { id: uuid(), // a random uuid generator function text: action.text, completed: false } ] case 'COMPLETE_TODO_SUCCESS': return state.map(todo => { if (todo.id === action.id) { return { ...todo, completed: true } } return todo }) default: return state } } const rootReducer = combineReducers({ user, todos }) const store = createStore(rootReducer)
状態からの読み取りと同様に、connect
を使用できます。アクションをディスパッチする関数。
/* UserProfile.js */ class Profile extends React.Component { handleSave(user) { this.props.updateUser(user); } } function mapDispatchToProps(dispatch) { return ({ updateUser: (user) => dispatch({ type: 'GET_USER_SUCCESS', user, }), }) } export default connect(mapStateToProps, mapDispatchToProps)(Profile);
リアクティブプログラミングは、「」のデータフローを処理する宣言型プログラミングパラダイムです。 ストリーム 」とその伝播と変化。 JavaScriptのリアクティブプログラミング用のライブラリであるRxJSには、次の概念があります。 観察可能 、オブザーバーができるデータのストリームです 申し込む に、そしてこのオブザーバーは時間の経過とともにデータを配信されます。
オブザーバブルのオブザーバブルは、next
、error
、およびcomplete
の3つの関数を持つオブジェクトです。これらの機能はすべてオプションです。
observable.subscribe({ next: value => console.log(`Value is ${value}`), error: err => console.log(err), complete: () => console.log(`Completed`), })
.subscribe
関数は、オブジェクトの代わりに3つの関数を持つこともできます。
observable.subscribe( value => console.log(`Value is ${value}`), err => console.log(err), () => console.log(`Completed`) )
observable
のオブジェクトを作成し、サブスクライバー、別名オブザーバーを受け取る関数を渡すことで、新しいオブザーバブルを作成できます。サブスクライバーには、next
、error
、およびcomplete
の3つの方法があります。サブスクライバーは、必要な回数だけ値を指定してnextを呼び出すことができ、complete
またはerror
最終的には。 complete
に電話した後またはerror
の場合、observableは値をストリームにプッシュしません。
import { Observable } from 'rxjs' const observable$ = new Observable(function subscribe(subscriber) { const intervalId = setInterval(() => { subscriber.next('hi'); subscriber.complete() clearInterval(intervalId); }, 1000); }); observable$.subscribe( value => console.log(`Value is ${value}`), err => console.log(err) )
上記の例では、Value is hi
が出力されます。 1000ミリ秒後。
毎回手動でオブザーバブルを作成することは、冗長で退屈になる可能性があります。したがって、RxJSには、オブザーバブルを作成するための多くの関数があります。最も一般的に使用されるものには、of
、from
、およびajax
があります。
of
一連の値を受け取り、それをストリームに変換します。
import { of } from 'rxjs' of(1, 2, 3, 'Hello', 'World').subscribe(value => console.log(value)) // 1 2 3 Hello World
from
ほとんどすべてのものを値のストリームに変換します。
ギリシャの金融危機の原因
import { from } from 'rxjs' from([1, 2, 3]).subscribe(console.log) // 1 2 3 from(new Promise.resolve('Hello World')).subscribe(console.log) // 'Hello World' from(fibonacciGenerator).subscribe(console.log) // 1 1 2 3 5 8 13 21 ...
ajax
文字列URLを受け取るか、HTTPリクエストを行うオブザーバブルを作成します。 ajax
関数ajax.getJSON
があり、ajax()
によって返される他のプロパティなしで、AJAX呼び出しからネストされた応答オブジェクトのみを返します。
import { ajax } from 'rxjs/ajax' ajax('https://jsonplaceholder.typicode.com/todos/1').subscribe(console.log) // {request, response: {userId, id, title, completed}, responseType, status} ajax.getJSON('https://jsonplaceholder.typicode.com/todos/1').subscribe(console.log) // {userId, id, title, completed} ajax({ url, method, headers, body }).subscribe(console.log) // {...}
オブザーバブルを作成する方法は他にもたくさんあります(完全なリストを見ることができます) ここに )。
オペレーターはRxJSの真のパワーハウスであり、必要なほとんどすべてのオペレーターがあります。 RxJS 6以降、演算子はオブザーバブルオブジェクトのメソッドではなく、.pipe
を使用してオブザーバブルに適用される純粋関数です。方法。
map
単一の引数関数を取り、ストリーム内の各要素に射影を適用します。
import { of } from 'rxjs' import { map } from 'rxjs/operators' of(1, 2, 3, 4, 5).pipe( map(i=> i * 2) ).subscribe(console.log) // 2, 4, 6, 8, 10
filter
単一の引数を取り、指定された関数に対してfalseを返す値をストリームから削除します。
import { of } from 'rxjs' import { map, filter } from 'rxjs/operators' of(1, 2, 3, 4, 5).pipe( map(i => i * i), filter(i => i % 2 === 0) ).subscribe(console.log) // 4, 16
flatMap
演算子は、スチーム内のすべてのアイテムを別のストリームにマップし、これらのストリームのすべての値をフラット化する関数を取ります。
import { of } from 'rxjs' import { ajax } from 'rxjs/ajax' import { flatMap } from 'rxjs/operators' of(1, 2, 3).pipe( flatMap(page => ajax.toJSON(`https://example.com/blog?size=2&page=${page}`)), ).subscribe(console.log) // [ { blog 1 }, { blog 2 }, { blog 3 }, { blog 4 }, { blog 5 }, { blog 6 } ]
merge
2つのストリームからのアイテムを到着順にマージします。
import { interval, merge } from 'rxjs' import { pipe, take, mapTo } from 'rxjs/operators' merge( interval(150).pipe(take(5), mapTo('A')), interval(250).pipe(take(5), mapTo('B')) ).subscribe(console.log) // A B A A B A A B B B
演算子の完全なリストが利用可能です ここに 。
設計上、Reduxのすべてのアクションは同期しています。 Redux-observableは、監視可能なストリームを使用して非同期作業を実行し、その非同期作業の結果を使用してReduxで別のアクションをディスパッチするReduxのミドルウェアです。
Redux-observableは次のアイデアに基づいています 叙事詩 。エピックは、アクションのストリーム、およびオプションで状態のストリームを取得し、アクションのストリームを返す関数です。
function(action $:Observable、state $:StateObservable):Observable;
慣例により、ストリーム(_aka _observable
)であるすべての変数は$
で終わります。 redux-observableを使用する前に、ストアにミドルウェアとして追加する必要があります。エピックはオブザーバブルのストリームであり、このスチームを出るすべてのアクションはパイプでストリームに戻されるため、同じアクションを返すと無限ループになります。
const epic = action$ => action$.pipe( filter(action => action.type === 'FOO'), mapTo({ type: 'BAR' }) // not changing the type of action returned // will also result in an infinite loop ) // or import { ofType } from 'redux-observable' const epic = action$ => action$.pipe( ofType('FOO'), mapTo({ type: BAZ' }) )
このリアクティブアーキテクチャは、すべてのパイプの出力が、それ自体を含むすべてのパイプと、Reduxのレデューサーにフィードバックされるパイプのシステムと考えてください。何が入り、何がブロックされるかを決定するのは、これらのパイプの上部にあるフィルターです。
ピンポンの叙事詩がどのように機能するか見てみましょう。 pingを受け取り、サーバーに送信します。リクエストが完了すると、pongをアプリに送り返します。
const pingEpic = action$ => action$.pipe( ofType('PING'), flatMap(action => ajax('https://example.com/pinger')), mapTo({ type: 'PONG' }) ) Now, we are going to update our original todo store by adding epics and retrieving users. import { combineReducers, createStore } from 'redux' import { ofType, combineEpics, createEpicMiddleware } from 'redux-observable'; import { map, flatMap } from 'rxjs/operators' import { ajax } from 'rxjs/ajax' // ... /* user and todos reducers defined as above */ const rootReducer = combineReducers({ user, todos }) const epicMiddleware = createEpicMiddleware(); const userEpic = action$ => action$.pipe( ofType('GET_USER'), flatMap(() => ajax.getJSON('https://foo.bar.com/get-user')), map(user => ({ type: 'GET_USER_SUCCESS', payload: user })) ) const addTodoEpic = action$ => action$.pipe( ofType('ADD_TODO'), flatMap(action => ajax({ url: 'https://foo.bar.com/add-todo', method: 'POST', body: { text: action.payload } })), map(data => data.response), map(todo => ({ type: 'ADD_TODO_SUCCESS', payload: todo })) ) const completeTodoEpic = action$ => action$.pipe( ofType('COMPLETE_TODO'), flatMap(action => ajax({ url: 'https://foo.bar.com/complete-todo', method: 'POST', body: { id: action.payload } })), map(data => data.response), map(todo => ({ type: 'COMPLEE_TODO_SUCCESS', payload: todo })) ) const rootEpic = combineEpics(userEpic, addTodoEpic, completeTodoEpic) const store = createStore(rootReducer, applyMiddleware(epicMiddleware)) epicMiddleware.run(rootEpic);
_重要:エピックは、RxJSの他の監視可能なストリームとまったく同じです。それらは、完全な状態またはエラー状態になる可能性があります。この状態が過ぎると、エピックとアプリは機能しなくなります。したがって、蒸気の潜在的なエラーをすべてキャッチする必要があります。 __catchError__
を使用できますこのための演算子。詳しくは: redux-observableでのエラー処理 。
いくつかのUIを追加すると、(最小限の)デモアプリは次のようになります。
リアクティブアプリとは何かを学びました。また、 戻ってきた 、RxJS、およびredux-observableであり、ReactNativeを使用してExpoでリアクティブTodoアプリを作成しました。ために React そして リアクトネイティブ 開発者、現在の傾向はいくつかの非常に強力な状態管理オプションを提供します。
もう一度、このアプリのソースコードはオンになっています GitHub 。以下のコメントで、リアクティブアプリの状態管理についての考えを自由に共有してください。
Reduxは、JavaScriptアプリ用の予測可能なグローバル状態コンテナーです。これは、状態管理と変更伝播ロジックをアプリケーションのReactコンポーネントから抽象化します。
リアクティブプログラミングは、データをストリームとして扱い、そのストリームを介して状態変化を伝播する宣言型プログラミングパラダイムです。
ReactiveXは、監視可能なストリームを使用した非同期プログラミング用のAPIです。これは、Observerパターン、Iteratorパターン、および関数型プログラミングからの最良のアイデアを組み合わせたものです。
RxJSは、JavaScriptのReactiveXAPIの実装ライブラリです。 JavaScriptの動的な性質と、広大で広範なReactiveXAPIを活用します。
Microsoftのコンピューター科学者であるErikMeijerは、ReactiveXAPIとその実装を.NETで作成しました。 RxJSは、さまざまなエンジニアによってRx.NETのポートとして作成されました。
RxJSは、関数型プログラミングで非同期タスクを処理するための広大なAPIを提供します。これにより、再試行、スロットリング、指数フォールバック、キャッシング、エラー処理などのアクションの使用が容易になります。
redux-observableは、Reduxで非同期タスクを処理するためのライブラリです。 Reduxからディスパッチされたアクションをキャプチャすることで機能し、非同期の作業を行います。作業が完了した後、 `redux-observable`は別のアクションをディスパッチすることにより、応答をRedux状態コンテナーに追加します。
RxJSはReactiveXの実装であり、任意の状態管理またはUIライブラリ、あるいはバニラJavaScriptで使用できます。監視可能なストリームの形式で非同期作業を管理するための強力なAPIを提供します。これらのストリームは、他のライブラリに接続することも、直接使用することもできます。 JavaScriptの外部では、reactivex.ioで一般的な言語のReactiveXの実装を見つけることができます。