Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to dispatch a thunk from a saga?

I know I shouldn't be trying to dispatch thunks from sagas, it goes against what redux-saga tries to do. But I'm working in a fairly large app and most of the code is made with thunks, we are migrating by bits and need to dispatch a thunk from inside a saga. The thunk can't be changed because it is used in other parts (a thunk that returns a promise), so it would break many things.

configureStore:

const store = createStore(
  rootReducer,
  initialState,
  compose(applyMiddleware(thunk, sagaMiddleware))
);

Saga:

// Saga (is called from a takeEvery)
function* watchWarehouseChange(action) {
  const companyId = yield select(Auth.id);

  // We use cookies here instead of localStorage so that we persist
  // it even when the user logs out. (localStorage clears on logout)
  yield call(Cookies.set, `warehouse${companyId}`, action.warehouse);

  // I want to dispatch a thunk here
  yield put.resolve(syncItems);
  // put(syncItems) doesn't work either
}

Thunk:

export function syncItems() {
  console.log('first!');

  return dispatch => {
    console.log('second!');

    return dispatch(fetchFromBackend()).then(
      items => itemsDB.emptyAndFill(items)
    )
  }
}

Whenever syncItems() is executed, only first! logs. second! never happens.

PS: I don't get any errors or warnings.

like image 833
Mathius17 Avatar asked Jun 02 '17 16:06

Mathius17


People also ask

How to dispatch thunks in Redux thunk?

We apply the thunk middleware, then we can dispatch thunks. This means that we can call dispatch with other thunks within a thunk, so we can compose them easily. Since Redux Thunk 2.1.0, we can use the withExtraArgument function to add an extra argument to the action that we return the function that we want to use with dispatch.

How do I dispatch a thunk with a selector?

Since dispatching a thunk returns the thunk return value, you could write a thunk that accepts a selector, and immediately calls the selector with the state and returns the result. This can be useful in a React component, where you have access to dispatch but not getState.

How does the thunk middleware work?

If you pass a function into dispatch, the thunk middleware sees that it's a function instead of an action object, intercepts it, and calls that function with (dispatch, getState) as its arguments If it's a normal action object (or anything else), it's forwarded to the next middleware in the chain

How to dispatch async actions into an app using thunk?

To dispatch async actions into our store, we have to apply the thunk middleware by writing: const store = createStore (joke, applyMiddleware (thunk)); to apply the middleware. Then in App, we call dispatch with the function returned from the fetchJoke passed inside. fetchJoke is an async action, which is also known as a thunk.


1 Answers

You're using syncItems wrong. The key is that the function returned by syncItems needs to get passed to dispatch, not syncItems itself. The correct usage would be:

yield put(syncItems());

I showed some visual comparisons of how values are passed into dispatch in my blog post Idiomatic Redux: Why use action creators? (based on an example gist I put together). Here's the examples:

// approach 1: define action object in the component
this.props.dispatch({
    type : "EDIT_ITEM_ATTRIBUTES", 
    payload : {
        item : {itemID, itemType},
        newAttributes : newValue,
    }
});

// approach 2: use an action creator function
const actionObject = editItemAttributes(itemID, itemType, newAttributes);
this.props.dispatch(actionObject);

// approach 3: directly pass result of action creator to dispatch
this.props.dispatch(editItemAttributes(itemID, itemType, newAttributes));

// parallel approach 1: dispatching a thunk action creator
const innerThunkFunction1 = (dispatch, getState) => {
    // do useful stuff with dispatch and getState        
};
this.props.dispatch(innerThunkFunction1);

// parallel approach 2: use a thunk action creator to define the function        
const innerThunkFunction = someThunkActionCreator(a, b, c);
this.props.dispatch(innerThunkFunction);

// parallel approach 3: dispatch thunk directly without temp variable        
this.props.dispatch(someThunkActionCreator(a, b, c));

In your case, just substitute yield put for this.props.dispatch, since you're dispatching from a saga instead of a connected component.

like image 138
markerikson Avatar answered Sep 19 '22 00:09

markerikson