Cequiel
Cequiel

Reputation: 3777

redux-saga: wait for dispatch to be completed

I'm using redux-saga to list and update a list of items stored in a remote server. And I would like to create an item and then to list the items. That is:

import { connect } from 'react-redux'

const NewItemDialog = ({ createItem, listItems }) => {
   const onAccept = () => {
       // this won't work, since 'dispatch' is synchronous
       await createItem('New item')
       listItems()
   }
   // ... snip ...
}

const mapStateToProps = (state) => {
    items: state.items
}

const mapDispatchToProps = (dispatch) => ({
    createItem: (name: string) => dispatch(createItem(name)),
    listItems: () => dispatch(listItems())
})

export default connect(mapStateToProps, mapDispatchToProps)(NewItemDialog)

What is the proper way to wait for an action to be completed?

Upvotes: 4

Views: 8031

Answers (2)

Cequiel
Cequiel

Reputation: 3777

I've being doing some research and probably redux-thunk handles these type of situations better than redux-action. For example, you can write something like:

createItem(name).then(() => {
    // redirects to the home page
    // after the item has been created
    history.push('/')
})

More information on this post:
https://github.com/reduxjs/redux/issues/1676#issuecomment-215413478

MORE INFO

This issue can be addressed in several ways. Here's another approach using redux-saga:

// sagas.js
// watcher function
export default function*() {
  // ... snip ...
  yield takeLatest(ITEM_CREATE, createItem)
  yield takeLatest(ITEM_UPDATE, updateItem)
  yield takeLatest(ITEM_DELETE, deleteItem)

  // wait until the item has been successfully
  // created, updated or deleted and **then** list the items
  yield takeEvery([
    ITEM_CREATE_SUCCESS,
    ITEM_UPDATE_SUCCESS,
    ITEM_DELETE_SUCCESS
  ], getItems)
}

// store.js
// ... snip ...
import sagas from './sagas'

// prepares the store and 'run' the watcher function
const sagaMiddleware = createSagaMiddleware()
const middleware = applyMiddleware(sagaMiddleware, thunk)
const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore(reducers, composeEnhancers(middleware))
sagaMiddleware.run(sagas)

In any case I still prefer redux-thunk because you can dispatch an arbitrary list of 'thunks' in any specific order.

Upvotes: 0

azundo
azundo

Reputation: 6052

Typically if you need to dispatch further actions you would put all of that into a single saga. For example if the createItem kicks off a saga that makes an api call and then needs to dispatch another action when that api call completes:


function* createItemSaga(action) {
  try {
    const response = yield call(makeApiCall, action.payload); // or however you call your api
    // yield any actions that depend on the response succeeding

  } catch(e) {
    // do some error handling here - maybe return early
  }
  yield put(listItems());
}

You'll have to decide on how you want code organized into reusable pieces if you don't always want to call put(listItems()) after a successful createItem call but something along these lines.

Upvotes: 2

Related Questions