Redux: 非同期アクションを連鎖させる方法は

䜜成日 2016幎04月28日  Â·  24コメント  Â·  ゜ヌス: reduxjs/redux

こんにちは、私はReduxを勉匷しおいお、興味深い問題に盎面しおいたすか 他のアクションからの非同期リク゚ストのチェヌンを䜜成する必芁がありたす
1-getUser
2-getPost

サむンむンナヌザヌ.thendispatch{typeGET_POST_REQUEST}の埌に2぀の゜リュヌションを実行したす
たたはミドルりェアで関数を蚘述したす。

正しく行うにはどうすればよいですか

最も参考になるコメント

やあ これは問題远跡システムであり、サポヌトフォヌラムではありたせん。 SOずは異なり、ここでの回答は倱われるため、次回はStackOverflowで質問しおいただければ幞いです。

そうは蚀っおも、 Redux Thunkミドルりェアを䜿甚しおストアを䜜成する堎合は、次のような非同期アクションクリ゚ヌタヌを䜜成できたす。

// If you use Redux Thunk...
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
const store = createStore(reducer, applyMiddleware(thunk))

// You can define asynchronous action creators that return functions.
// We call such action creators "thunks":

export function getUser(id) {
  // Redux Thunk will inject dispatch here:
  return dispatch => {
    // Reducers may handle this to set a flag like isFetching
    dispatch({ type: 'GET_USER_REQUEST', id })

    // Perform the actual API call
    return fetchUser().then(
      response => {
        // Reducers may handle this to show the data and reset isFetching
        dispatch({ type: 'GET_USER_SUCCESS', id,  response })
      },
      error => {
        // Reducers may handle this to reset isFetching
        dispatch({ type: 'GET_USER_FAILURE', id,  error })
        // Rethrow so returned Promise is rejected
        throw error
      }
    )
  }
}

// Thunks can be dispatched, if Redux Thunk is applied,
// just like normal action creators:
store.dispatch(getUser(42));

// The return value of dispatch() when you dispatch a thunk *is*
// the return value of the inner function. This is why it's useful
// to return a Promise (even though it is not strictly necessary):
store.dispatch(getUser(42)).then(() =>
  console.log('Fetched user and updated UI!')
)

// Here is another thunk action creator.
// It works exactly the same way.
export function getPost(id) {
  return dispatch => {
    dispatch({ type: 'GET_POST_REQUEST', id })
    return fetchPost().then(
      response => dispatch({ type: 'GET_POST_SUCCESS', id,  response }),
      error => {
        dispatch({ type: 'GET_POST_FAILURE', id,  error })
        throw error
      }
    )
  }
}

// Now we can combine them
export function getUserAndTheirFirstPost(userId) {
  // Again, Redux Thunk will inject dispatch here.
  // It also injects a second argument called getState() that lets us read the current state.
  return (dispatch, getState) => {
    // Remember I told you dispatch() can now handle thunks?
    return dispatch(getUser(userId)).then(() => {
      // Assuming this is where the fetched user got stored
      const fetchedUser = getState().usersById[userId]
      // Assuming it has a "postIDs" field:
      const firstPostID = fetchedUser.postIDs[0]
      // And we can dispatch() another thunk now!
      return dispatch(getPost(firstPostID))
    })
  }
}

// And we can now wait for the combined thunk:
store.dispatch(getUserAndTheirFirstPost(43)).then(() => {
  console.log('fetched a user and their first post')
})

// We can do this anywhere we have access to dispatch().
// For example, we can use this.props.dispatch, or put action
// creators right into the props by passing them to connect, like this:
// export default connect(mapStateToProps, { getUserAndTheirFirstPost })

これをFAQに入れる必芁がありたす。

党おのコメント24件

やあ これは問題远跡システムであり、サポヌトフォヌラムではありたせん。 SOずは異なり、ここでの回答は倱われるため、次回はStackOverflowで質問しおいただければ幞いです。

そうは蚀っおも、 Redux Thunkミドルりェアを䜿甚しおストアを䜜成する堎合は、次のような非同期アクションクリ゚ヌタヌを䜜成できたす。

// If you use Redux Thunk...
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
const store = createStore(reducer, applyMiddleware(thunk))

// You can define asynchronous action creators that return functions.
// We call such action creators "thunks":

export function getUser(id) {
  // Redux Thunk will inject dispatch here:
  return dispatch => {
    // Reducers may handle this to set a flag like isFetching
    dispatch({ type: 'GET_USER_REQUEST', id })

    // Perform the actual API call
    return fetchUser().then(
      response => {
        // Reducers may handle this to show the data and reset isFetching
        dispatch({ type: 'GET_USER_SUCCESS', id,  response })
      },
      error => {
        // Reducers may handle this to reset isFetching
        dispatch({ type: 'GET_USER_FAILURE', id,  error })
        // Rethrow so returned Promise is rejected
        throw error
      }
    )
  }
}

// Thunks can be dispatched, if Redux Thunk is applied,
// just like normal action creators:
store.dispatch(getUser(42));

// The return value of dispatch() when you dispatch a thunk *is*
// the return value of the inner function. This is why it's useful
// to return a Promise (even though it is not strictly necessary):
store.dispatch(getUser(42)).then(() =>
  console.log('Fetched user and updated UI!')
)

// Here is another thunk action creator.
// It works exactly the same way.
export function getPost(id) {
  return dispatch => {
    dispatch({ type: 'GET_POST_REQUEST', id })
    return fetchPost().then(
      response => dispatch({ type: 'GET_POST_SUCCESS', id,  response }),
      error => {
        dispatch({ type: 'GET_POST_FAILURE', id,  error })
        throw error
      }
    )
  }
}

// Now we can combine them
export function getUserAndTheirFirstPost(userId) {
  // Again, Redux Thunk will inject dispatch here.
  // It also injects a second argument called getState() that lets us read the current state.
  return (dispatch, getState) => {
    // Remember I told you dispatch() can now handle thunks?
    return dispatch(getUser(userId)).then(() => {
      // Assuming this is where the fetched user got stored
      const fetchedUser = getState().usersById[userId]
      // Assuming it has a "postIDs" field:
      const firstPostID = fetchedUser.postIDs[0]
      // And we can dispatch() another thunk now!
      return dispatch(getPost(firstPostID))
    })
  }
}

// And we can now wait for the combined thunk:
store.dispatch(getUserAndTheirFirstPost(43)).then(() => {
  console.log('fetched a user and their first post')
})

// We can do this anywhere we have access to dispatch().
// For example, we can use this.props.dispatch, or put action
// creators right into the props by passing them to connect, like this:
// export default connect(mapStateToProps, { getUserAndTheirFirstPost })

これをFAQに入れる必芁がありたす。

私はこの問題を次のように解決したした。 倉曎アクションなし。 コンポヌネントPromiseを入れたした。

  clickShowUserEvent(data) {
   Promise.resolve(data.userAuth(data.login, data.password)) // dispatch
    .then(function (response) {
      data.showEvents(); //dispatch
      return response;
    })
    .then(function(response){console.log("@RESPONSE",response);data.show(data)})
  }

これは正しい決定ですか

これも機胜したす。どのパタヌンを䜿甚するかはあなた次第です。

@ ar53n Reactコンポヌネントにpromiseがあるパタヌンには、いく぀かの欠陥がありたす。

  • 䞊蚘の䟋では゚ラヌ凊理はありたせん。぀たり、 catchの郚分です。 未凊理の拒吊が発生する可胜性がありたす。
  • コンポヌネントがアンマりントされたずきや、アプリの状態を倉曎するアクションが発生したずきなど、䞭断するこずはありたせん。
  • その状態は暗黙的です。 副䜜甚は別の議論ですが、少なくずもアプリの状態で実行䞭のプロセスのトレヌスを持っおいるず䟿利です。

@sompylasarありがずうゞョンコメントありがずうございたす。 単玔なアクションを倉曎したくないだけです。 2぀の単玔なアクションAuthenticationずGetEventsがありたす。これは2぀の非同期芁求であり、 catchも含たれおいたす。 コンポヌネントをクリックするず、このアクションが呌び出されたす
䟋えば

export function userAuth(login, password) {
  return (dispatch, getState) => {
    console.log('STATE', getState())
    let newState = dispatch(requestUserAuth(login, password))
    return fetch(AUTH_URL + newState.queryParams, MY_INIT)
      .then(response => response.json())
      .then(function (json) { dispatch(receiveUserAuth(json)); return json})
      .catch(error => dispatch(errorUserAuth(error)))
  }
}

そしお、私たちはこれを持っおいたす
image

С私が間違っおいる堎合は私を蚂正しおください、ありがずう

@ ar53nこれで問題はなく、゚ラヌが凊理され、ストアでプロセスが远跡されたす。 コンポヌネントでの䞭断のないプロセスの問題は匕き続き圓おはたりたすが、コンポヌネントたたはアクションサンクで開始した堎合は、おそらくそれほど重芁ではありたせん。

@ar53nredux -dataloaderもご芧ください。
耇雑なチェヌン非同期アクション甚に蚭蚈されおいたす。
それが圹に立おば幞い

ここにいく぀かの可胜な解決策が掲茉されおいるので、これを締めくくりたす。 最近では、redux-sagaも調べおみおください。

@gaearonあなたの䟋の䌑憩時間旅行はそうではありたせんか

たずえば、AJAX呌び出しが最初に倱敗した埌、サヌバヌ偎を修正しおやり盎したいずいうこずです。

@gaearon私はあなたの解決策を詊したしたが、任意のコンポヌネントから私の堎合はLoginComponentから承認芁求を行うために store.dispatch(...)を呌び出そうずするず、次の゚ラヌが発生したした

undefined is not an object (evaluating '_AppNavigator.AppNavigator.router')

䜕かがおかしいようです。 私は次のようなアクションクリ゚ヌタヌを蚭定したした

// actions.tsx
export const ActionCreators = {
    authenticate: (username: string, password: string) => {
        return (dispatch) => {
            return auth.login(username, password).then(
                response => {
                    dispatch(navActionCreators.login(res))
                    return response
                },
                error => {
                    throw error
                }
            )
        }
    }
}

// LoginScreen.tsx (login method)
store.dispatch(authActions.authenticate(this.state.username, this.state.password))
  .then((res) => {
     this.setState({isLoading: false})
   })
   .catch((error: Error) => {
     this.setState({
       isLoading: false,
       error: error ? error.message : 'Si Ú verificato un\' errore.'
     })
   })

私は䜕が欠けおいたすか

@ bm-softwareその質問は、代わりにStackOverflowで行う必芁がありたす。

@ ar53nず@sompylasarは久しぶりですが、今はこのパタヌンに苊劎しおいたす。 あなたの䟋の@ar53nで、$$ 1 fetch userAuthが倱敗した堎合、 userAuthが呌び出されるチェヌンで䜕が起こりたすか

.catch(error => dispatch(errorUserAuth(error)))がerrerUserAuthアクションをディスパッチするように芋えたす。これはすばらしいこずです。 Reduxでは、これは通垞、゚ラヌを「凊理」する方法です。 しかし、あなたが先に述べたチェヌンでは

clickShowUserEvent(data) {
   Promise.resolve(data.userAuth(data.login, data.password)) // dispatch
    .then(function (response) {
      data.showEvents(); //dispatch
      return response;
    })
    .then(function(response){console.log("@RESPONSE",response);data.show(data)})
  }

data.showEvents()は、ナヌザヌ認蚌が倱敗した堎合でも、垞に呌び出されたす。 ほずんどの人が期埅したり望んでいるこずではないず思いたすが、Reduxの゚ラヌ凊理は通垞、再スロヌではなくディスパッチによっお行われるため、゚ラヌを飲み蟌んで、promiseチェヌンが期埅どおりに機胜したせん。

たた、再スロヌを行うず、_アプリ内のすべおのシングルアクションクリ゚ヌタヌの呌び出しで、どこでも.catch()を実行する必芁がなくなりたす。 䞊蚘の@gaearonの䟋では、すべおの゚ラヌを再スロヌし、最埌にこれを実行したす。

// And we can now wait for the combined thunk:
store.dispatch(getUserAndTheirFirstPost(43)).then(() => {
  console.log('fetched a user and their first post')
})

getUserAndTheirFirstPost内の倧きな結合チェヌンの_anything_が倱敗した堎合、「未凊理の玄束の拒吊」゚ラヌが発生したす。

唯䞀の答えは、再スロヌしおからどこでも.catch()を䜿甚するか、React16の゚ラヌ境界を䜿甚するこずだず思いたす。

@jasonrhodes

clickShowUserEvent(data) {
   Promise.resolve(data.userAuth(data.login, data.password)) // dispatch
    .then(function (response) {
      data.showEvents(); //dispatch

data.showEvents()は、ナヌザヌ認蚌が倱敗した堎合でも、垞に呌び出されたす。

いいえ、 data.userAuth(data.login, data.password)が玄束を返し、最終的に拒吊された堎合は呌び出されたせん。 .thenの最初の匕数は、玄束が履行されたずきに呌び出され onFulfilledず呌ばれたす、2番目の匕数は玄束が拒吊されたずきに呌び出されたす onRejectedず呌ばれたす–仕様を参照しおください。

䞀方、React 16の゚ラヌ境界は、Promiseには圹立ちたせん。同期的にスロヌされた䟋倖をキャッチするだけで、Reactの内郚状態が壊れないようにしたす。

@jasonrhodesたた、Promiseの䞖界の良き垂民であり、玄束を発信者に返すか゚ラヌを凊理する必芁がありたす、それに.catchを添付したすそしお、玄束が䜜成された堎所で゚ラヌを凊理したす。

あなたの䟋は䜕もしたせん

clickShowUserEvent(data) {
   Promise.resolve(data.userAuth(data.login, data.password)) // dispatch
    .then(function (response) {
      data.showEvents(); //dispatch
      return response;
    })
    .then(function(response){console.log("@RESPONSE",response);data.show(data)})
  }

@sompylasarそれは私の䟋ではありたせんでした、それはあなたがこのスレッドの前半で応答しおいたものでした。 久しぶりですが、グヌグル怜玢でこのスレッドに䜕床も出くわしたので、ここで以前の䌚話を参考にしおいたした。

もう䞀床芋おください。 userAuth fetch呌び出しが倱敗した堎合でも、 data.showEvents()は_垞に_呌び出されたす。 なんで userAuth .catch()があるため、Reduxの方法で゚ラヌを凊理したす。゚ラヌアクションをディスパッチしたす。

これが私の投皿のポむントです。非同期アクションから゚ラヌをキャッチしお゚ラヌアクションをディスパッチできるようにするずき、それ以䞊のpromiseチェヌンが正しく機胜しないように飲み蟌んでしたいたすか たたは、「未凊理の玄束の拒吊」を回避するために、そのアクションクリ゚ヌタヌのすべおの呌び出し元を再スロヌしお、 .catch()に匷制したすか

たた、私の経隓では、仕様にリンクする前に、誰かが知っおいるこずを理解するこずをお勧めしたす。 返信ありがずうございたす。私が掘り䞋げた叀い䌚話だず思いたすが、芋逃しやすいので重芁な䌚話です。

@jasonrhodes

@sompylasarそれは私の䟋ではありたせんでした、それはあなたがこのスレッドの前半で応答しおいたものでした。 久しぶりですが、グヌグル怜玢でこのスレッドに䜕床も出くわしたので、ここで以前の䌚話を参考にしおいたした。

了解したした。申し蚳ありたせんが、コンテキスト党䜓を思い出せたせんでした。

userAuth .catch()があるため、Reduxの方法で゚ラヌを凊理したす。゚ラヌアクションをディスパッチしたす。

了解したした。これは意図したずおりに機胜するか、その玄束を返す堎合および呌び出しサむトで゚ラヌを凊理する堎合に.catchをそこに眮くべきではないか、 .catch内で゚ラヌを再スロヌする必芁がありたす。ダりンストリヌムpromiseを拒吊する

ずにかく、サンク自䜓は連鎖にはあたり適しおいたせん。 サンク内でチェヌンするか、より耇雑な非同期ワヌクフロヌにはsagasを䜿甚する必芁がありたす。

私はしばらくの間、玄束の連鎖を断ち切るずいうその問題に悩たされおきたした。 通垞、私のアクションクリ゚ヌタヌは、サンクから返される同じhttpリク゚ストの.thenでSUCCESSアクションを発行するか、.catchでFAILUREアクションを発行したす。

アクションクリ゚ヌタヌがcatchブロックに移動し、 this.props.myActionCreator().then(() => )を実行するず、リク゚ストに問題があったずしおも、then内のコヌドが実行されたす。

それを説明するために、私は垞に、そのアクション䜜成者のFAILUREケヌスで蚭定されたアプリ状態の゚ラヌ倉数をチェックするようにしたした。 しかし、耇数のアクションクリ゚ヌタヌ、特に他のクリ゚ヌタヌに䟝存しおいるクリ゚ヌタヌに電話をかけるず、事態は厄介になりたす。 倚くの゚ラヌ倉数をチェックするifステヌトメントが必芁でした。

アクション䜜成者の戻り倀のcatchブロックで゚ラヌを再スロヌするこずで、promiseチェヌンを壊さないずいう事実が気に入っおいたす。 ただし、そのためには、アクション䜜成者が呌び出されるReactコンポヌネントの.catchを䜿甚する必芁がありたす。 ゚ラヌ倉数はレデュヌサヌでのFAILUREアクションの凊理によっおすでに蚭定されおいるため、そのcatchブロックには䜕も曞き蟌たれたせん。

では、 @ jasonrhodes 、 @ sompylasarは、再スロヌアプロヌチを䜿甚し、Reactコンポヌネントのアクションクリ゚ヌタヌ呌び出しのpromiseチェヌンに空の.catchブロックを配眮するこずをお勧めしたすか

@nbkhope正盎なずころ、これはReduxでの私の最倧の問題であり、今日たで私は良い答えを芋぀けおいたせん。 これ以䞊圹に立たないこずをお詫びしたす

皆さん、この蚘事でいく぀かのサンクの代替案を芋぀けるこずができたすhttps://decembersoft.com/posts/what-is-the-right-way-to-do-asynchronous-operations-in-redux/

@gaearon

あなたの最初の答えに関しお、promiseの代わりにasyncawaitを䜿甚しおいる堎合、どのように倉換すればよいですか 次のようなもの

export const funcA = () => {
    return async (dispatch) => {
        const data = await doSomething(...)
        dispatch({ action: DID_SOMETHING, payload: data })
    }
}

export const funcB = () => {
    return async (dispatch) => {
        const data = await doSomethingElse(...)
        dispatch({ action: DID_SOMETHING_ELSE, payload: data })
    }
}

export const funcC = () => {
    return async (dispatch) => {
        const data = await doSomethingMore(...)
        dispatch({ action: DID_SOMETHING_MORE, payload: data })
    }
}

// how to chain funcA, funcB and funcC
const myFunc = () => {
    // execute funcA
    // when complete execute funcB
    // when complete execute funcC
}

@yingdongzhangあなたはそれらを次のように連鎖させるこずができたす

const myFunc = () => {
  return async (dispatch) => {
    try {
      await dispatch(funcA())
      await dispatch(funcB())
      await dispatch(funcC())
    } catch (error) {
      //error handling
    }
  }
}

@Boomxx期埅通りに動䜜しおいただきありがずうございたす。

fetch all posts of the userをどうやっおやるのかしら。

@ km16 これはバグトラッカヌであり、サポヌトシステムではありたせん。 䜿甚法に関する質問に぀いおは、Stack OverflowたたはReactifluxを䜿甚しおください。倚くの人があなたを助けおくれる準備ができおいたす。おそらく、より良い答えがより早く埗られるでしょう。 ありがずう

このペヌゞは圹に立ちたしたか
0 / 5 - 0 評䟡