React/Reduxは初めてです。私はAPIを処理するためにReduxアプリでフェッチAPIミドルウェアを使用します。それは https://github.com/agraboso/redux-api-middleware です。非同期APIアクションを処理するのは良い方法だと思います。しかし、私は自分では解決できないいくつかのケースを見つけます。
ホームページ https://github.com/agraboso/redux-api-middleware#lifecycle によると、フェッチAPIのライフサイクルはCALL_APIアクションのディスパッチで始まり、FSAアクションのディスパッチで終わります。
だから私の最初のケースは、APIを取得するときにプリローダーを表示/非表示にすることです。ミドルウェアは最初にFSAアクションをディスパッチし、最後にFSAアクションをディスパッチします。どちらのアクションも、通常のデータ処理を実行するだけのはずのリデューサーによって受け取られます。 UI操作がなく、操作も不要です。たぶん私は処理の状態をstateに保存し、ストアの更新時にそれらをレンダリングするべきです。
しかし、これをどのように行うのですか?ページ全体に反応するコンポーネントが流れますか。他のアクションからストアを更新するとどうなりますか?私は彼らが州よりもイベントに近いということを意味します!
最悪の場合でも、redux/reactアプリでネイティブの確認ダイアログまたは警告ダイアログを使用する必要がある場合はどうすればよいですか。それらはどこに置くべきですか、行動または減力剤?
ご多幸を祈る!返信してほしい。
私は彼らが州よりもイベントに近いということを意味します!
私はそうは思わないでしょう。ローディングインジケータは、状態の関数、つまりこの場合はブール変数の関数として簡単に説明できるUIの優れたケースです。 この答え は正しいのですが、それに合わせてコードをいくつか提供したいと思います。
Redux repoのasync
の例 、reducer isFetching
というフィールドを更新します :
case REQUEST_POSTS:
return Object.assign({}, state, {
isFetching: true,
didInvalidate: false
})
case RECEIVE_POSTS:
return Object.assign({}, state, {
isFetching: false,
didInvalidate: false,
items: action.posts,
lastUpdated: action.receivedAt
コンポーネントはReact Reduxのconnect()
を使用してストアの状態をサブスクライブし、 mapStateToProps()
の戻り値の一部としてisFetching
を返す を使用するので、接続されたコンポーネントの小道具で使用できます。
function mapStateToProps(state) {
const { selectedReddit, postsByReddit } = state
const {
isFetching,
lastUpdated,
items: posts
} = postsByReddit[selectedReddit] || {
isFetching: true,
items: []
}
return {
selectedReddit,
posts,
isFetching,
lastUpdated
}
}
最後に、コンポーネント render()
関数でisFetching
propを使用 “ Loading ...”ラベルをレンダリングする(代わりにスピナーにすることもできます):
{isEmpty
? (isFetching ? <h2>Loading...</h2> : <h2>Empty.</h2>)
: <div style={{ opacity: isFetching ? 0.5 : 1 }}>
<Posts posts={posts} />
</div>
}
最悪の場合でも、redux/reactアプリでネイティブの確認ダイアログまたは警告ダイアログを使用する必要がある場合はどうすればよいですか。それらはどこに置くべきですか、行動または減力剤?
どんな副作用(そしてダイアログを表示することはほとんど確実に副作用です)は減力剤に属しません。減力剤を受動的な「国家の建設者」と考えてください。彼らは本当に物事を「する」のではありません。
警告を表示したい場合は、アクションをディスパッチする前にコンポーネントから実行するか、アクション作成者から実行してください。アクションがディスパッチされるまでに、それに応答して副作用を実行するには遅すぎます。
すべての規則について、例外があります。時にはあなたの副作用ロジックが非常に複雑なので、実際にはそれらを特定のアクションタイプか特定のリデューサーに結合したいことを望みます。この場合、 Redux Saga および Redux Loop のような高度なプロジェクトをチェックしてください。あなたがVanilla Reduxに慣れていて、あなたがより扱いやすいものにしたいと思う散在する副作用の本当の問題を抱えているときだけこれをしてください。
素晴らしい回答Dan Abramov!私のアプリケーションの1つで多少正確に同じことをしていて(isFetchingをブール値として保持している)、複数の同時をサポートするために整数(未解決の要求数として読み取ることになる)リクエスト。
ブール値の場合:
リクエスト1が開始 - >スピナーがオン - >リクエスト2が開始 - >リクエスト1が終了 - >スピナーがオフ - >リクエスト2が終了
整数の場合
リクエスト1が開始 - >スピナーがオン - >リクエスト2が開始 - >リクエスト1が終了 - >リクエスト2が終了 - >スピナーがオフ
case REQUEST_POSTS:
return Object.assign({}, state, {
isFetching: state.isFetching + 1,
didInvalidate: false
})
case RECEIVE_POSTS:
return Object.assign({}, state, {
isFetching: state.isFetching - 1,
didInvalidate: false,
items: action.posts,
lastUpdated: action.receivedAt
何か追加したいのですが。実世界の例では、ストア内のフィールドisFetching
を使用して、項目のcollectionがフェッチされている時期を表しています。どのコレクションもpagination
リデューサーに一般化され、コンポーネントに接続して状態を追跡し、コレクションがロードされているかどうかを確認できます。
改ページパターンに収まらない特定のエンティティの詳細を取得したいと思いました。詳細がサーバーから取得されているかどうかを表す状態を持つことを望みましたが、それだけのためにリデューサーを取得することもしたくありませんでした。
これを解決するために、私はfetching
と呼ばれるもう一つの一般的な減数を追加しました。これはページネーションリデューサーと同じように機能し、その役割は監視一連のアクションを実行し、ペアで新しい状態を生成することだけです[entity, isFetching]
。これにより、任意のコンポーネントへのリデューサーをconnect
すること、およびアプリが現在コレクションだけでなく特定のエンティティについてデータをフェッチしているかどうかを知ることができます。
私は今までこの質問には起こりませんでした、しかし答えが受け入れられないので私は私の帽子を投げます。私はこのまさにその仕事のためのツールを書きました: react-loader-factory 。それはAbramovの解決策よりもやや進行していますが、書いた後で考える必要がないので、よりモジュール化されていて便利です。
4つの大きな部分があります。
const loaderWrapper = loaderFactory(actionsList, monitoredStates);
connect()
がReduxで返すもののような)「高次コンポーネント」であるため、既存のものに追加することができます。 const LoadingChild = loaderWrapper(ChildComponent);
ACTION_SUCCESS
およびACTION_REQUEST
をディスパッチする方法)。 (もちろん、他の場所にアクションをディスパッチし、必要に応じてラッパーから監視することもできます。)モジュール自体はredux-api-middlewareから独立していますが、それが私が使用しているものなので、READMEのサンプルコードを次に示します。
それをラップするローダーを持つコンポーネント:
import React from 'react';
import { myAsyncAction } from '../actions';
import loaderFactory from 'react-loader-factory';
import ChildComponent from './ChildComponent';
const actionsList = [myAsyncAction()];
const monitoredStates = ['ASYNC_REQUEST'];
const loaderWrapper = loaderFactory(actionsList, monitoredStates);
const LoadingChild = loaderWrapper(ChildComponent);
const containingComponent = props => {
// Do whatever you need to do with your usual containing component
const childProps = { someProps: 'props' };
return <LoadingChild { ...childProps } />;
}
ローダーが監視するためのリデューサー(あなたがすることができますが 違う方法で配線する =)
export function activeRequests(state = [], action) {
const newState = state.slice();
// regex that tests for an API action string ending with _REQUEST
const reqReg = new RegExp(/^[A-Z]+\_REQUEST$/g);
// regex that tests for a API action string ending with _SUCCESS
const sucReg = new RegExp(/^[A-Z]+\_SUCCESS$/g);
// if a _REQUEST comes in, add it to the activeRequests list
if (reqReg.test(action.type)) {
newState.Push(action.type);
}
// if a _SUCCESS comes in, delete its corresponding _REQUEST
if (sucReg.test(action.type)) {
const reqType = action.type.split('_')[0].concat('_REQUEST');
const deleteInd = state.indexOf(reqType);
if (deleteInd !== -1) {
newState.splice(deleteInd, 1);
}
}
return newState;
}
近い将来、タイムアウトやエラーなどをモジュールに追加する予定ですが、パターンはそれほど変わらないでしょう。
あなたの質問に対する簡単な答えは:
ロード指標がReduxストアに属していないと私が思うのは私だけですか?つまり、アプリケーション自体の状態の一部ではないと思います。
今、私はAngular 2を使用しています。RxJSBehaviourSubjectsを介してさまざまなローディングインジケータを公開する「ローディング」サービスを使用しています。メカニズムは同じだと思います。Reduxには情報を格納しません。
LoadingServiceのユーザーは、聴きたいイベントを購読するだけです。
私のReduxアクション作成者は、状況を変える必要があるときはいつでもLoadingServiceを呼び出します。 UXコンポーネントは公開されている観測量を購読しています...
アプリには3種類の通知があります。これらはすべてアスペクトとして設計されています。
これらの3つすべてが我々のアプリのトップレベル(Main)にあり、以下のコードスニペットに示すようにReduxを通して配線されています。これらの小道具は対応するアスペクトの表示を制御します。
私はすべてのAPI呼び出しを処理するプロキシを設計しました。したがって、すべてのisFetchingおよび(api)エラーは、プロキシにインポートしたactionCreatorsによって仲介されます。 (余談ですが、私はwebpackを使ってdev用のバッキングサービスのモックを注入しているので、サーバーに依存せずに作業することができます。)
あらゆる種類の通知を提供する必要があるアプリ内の他の場所は、単に適切なアクションをインポートするだけです。 Snackbar&Errorはメッセージを表示するためのパラメータを持っています。
@connect(
// map state to props
state => ({
isFetching :state.main.get('isFetching'), // ProgressIndicator
notification :state.main.get('notification'), // Snackbar
error :state.main.get('error') // ErrorPopup
}),
// mapDispatchToProps
(dispatch) => { return {
actions: bindActionCreators(actionCreators, dispatch)
}}
)デフォルトクラスをエクスポートするMainはReact.Componentを拡張する
React Reduxのconnect()
または低レベルのstore.subscribe()
メソッドを使用して、ストアに変更リスナーを追加できます。ストアにローディングインジケータがあるはずです。ストア変更ハンドラはそれを確認してコンポーネントの状態を更新できます。その後、コンポーネントは状態に基づいて、必要に応じてプリローダーをレンダリングします。
alert
とconfirm
は問題になりません。それらはブロックされていて、alertはユーザーからの入力も取りません。 confirm
を使用すると、ユーザーの選択がコンポーネントのレンダリングに影響する場合は、ユーザーがクリックした内容に基づいて状態を設定できます。そうでない場合は、後で使用するために選択内容をコンポーネントメンバー変数として格納できます。
::のようなURLを保存しています
isFetching: {
/api/posts/1: true,
api/posts/3: false,
api/search?q=322: true,
}
それから私は(再選択を介して)記憶されたセレクタを持っています。
const getIsFetching = createSelector(
state => state.isFetching,
items => items => Object.keys(items).filter(item => items[item] === true).length > 0 ? true : false
);
POSTの場合にURLを一意にするために、クエリとして変数を渡します。
そして私がインディケータを見せたいところで、私は単にgetFetchCount変数を使います