状況
ユーザーが段階的なオンボーディングを行うオンボーディング シナリオがあります。Redux を使用して、ユーザーの進行状況のクライアント側の状態を管理したいと考えています。サーバーとクライアント間の同期はすでに Relay に実装されていますが、クライアント側の状態管理には Redux ストアが必要です。そのため、Relay-/Redux-Store の同期で問題が発生します。
私が今行っていることは、React コンポーネントを Redux でラップしてから Relay でラップすることです。
// OnboardProgressView.js
// ...
// wrap React component with Redux
const mapStateToProps = (state) => {
return {
onboardProgress: state.onboardProgress,
}
}
const ReduxContainer = connect(
mapStateToProps,
)(OnboardProgressView)
// this is only for convenience of access of the Relay data
const MappedOnboardProgressView = mapProps({
params: (props) => props.params,
user: (props) => props.viewer.user,
})(ReduxContainer)
// wrap Redux component with Relay
export default Relay.createContainer(MappedGettingStartedView, {
fragments: {
viewer: () => Relay.QL`
fragment on Viewer {
user {
userId
onboardProgressStep
}
# more stuff ...
}
`,
},
})
私の進歩
次のように、さまざまな操作を実行する方法を見つけました。
サーバーデータを使用した Redux ストアの初期化
非同期の生リレー クエリを使用してストアを作成した直後に、Redux の状態を初期化しています。それを可能にするために、redux-thunk
ミドルウェアも使用しています。Redux は、サーバーに問い合わせる Relay へのリクエストを開始します。視覚的表現 (矢印はデータ フローを示し、要素の順序は「呼び出し順序」を反映しています):Redux <= Relay <= Server
// app.js
const store = createStore(reducer, applyMiddleware(thunk))
store.dispatch(fetchOnboardProgress())
// onboardProgress.js
export function fetchOnboardProgress () {
return function (dispatch) {
var query = Relay.createQuery(Relay.QL`
query {
viewer {
user {
id
onboardProgress
}
}
}`, {})
return new Promise(function (resolve, reject) {
Relay.Store.primeCache({query}, ({done, error}) => {
if (done) {
const data = Relay.Store.readQuery(query)[0]
dispatch(update(data.user.onboardProgress, data.user.id))
resolve()
} else if (error) {
reject(Error('Error when fetching onboardProgress'))
}
})
})
}
}
Redux アクションのディスパッチ時にサーバー上のデータを更新する
Redux => Relay => Server
状態の変化に一貫性を持たせるために、ユーザーがオンボーディング プロセスを進めるときに Redux アクションを起動します。このアクションは、Relay ミューテーションも非同期で実行します。私もredux-thunk
この目的で使用しています。
function nextStep () {
return function (dispatch, getState) {
const currentStep = getState().onboardProgress.step
const currentStepIndex = OnboardProgress.steps.indexOf(currentStep)
const nextStep = OnboardProgress.steps[currentStepIndex + 1]
const userId = getState().onboardProgress._userId
return _updateReduxAndRelay(dispatch, nextStep, userId)
}
}
function _updateReduxAndRelay (dispatch, step, userId) {
return new Promise((resolve, reject) => {
Relay.Store.commitUpdate(new UpdateUserMutation({
userId: userId,
onboardProgressStep: step,
}), {
onSuccess: () => {
dispatch(update(step, userId))
resolve()
},
onFailure: reject,
})
})
}
export function update (step, userId) {
const payload = {onboardProgress: new OnboardProgress({step, userId})}
return {type: UPDATE, payload}
}
未解決の問題
私はまだ次の状況へのアプローチを見つけていません:
Relay Store の更新時に Redux Store を更新する
サーバー上のデータへの変更には、アプリ内のユーザー アクションによってトリガーされない外部ソースが含まれる場合があります。Relay を使用すると、これを forceFetching またはポーリングで解決できます。リレー クエリは次のようになりますRelay <= Server
。このデータ フローを追加したいと思います:Relay => Redux
外部データが変更されたとき。
Redux ストアを新しいデータで更新する必要があるもう 1 つの理由は、Relay ストアに深くネストされているデータ、または複雑なクエリの一部を同期する場合です。
たとえば、ブログ投稿へのコメント数を考えてみてください。ユーザーが新しいコメントを投稿すると、コメント数を表示する別のコンポーネントも更新されます。
この情報を Redux で管理する場合、Relay クエリに新しい情報が含まれているときに Redux アクションをトリガーする方法が必要です。このようなコールバックや、この状況に対する別の解決策については知りません。
私の質問
この文脈で、私はそれらの質問があります:
- 既存のアプローチで何を改善できますか? 非常に危険な/矛盾につながるようなことをしたことがありますか? (私の進捗状況を参照)
- 何らかの理由で Relay ストアが更新されているときに Redux ストアを同期するにはどうすればよいですか。Redux アクションを Redux ストアに送信できる React コンポーネントのライフサイクル メソッドまたは Relay コールバックを探しています。(未解決の問題を参照)