最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to dispatch a thunk from a saga? - Stack Overflow

programmeradmin4浏览0评论

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,
  pose(applyMiddleware(thunk, sagaMiddleware))
);

Saga:

// Saga (is called from a takeEvery)
function* watchWarehouseChange(action) {
  const panyId = 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${panyId}`, 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.

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,
  pose(applyMiddleware(thunk, sagaMiddleware))
);

Saga:

// Saga (is called from a takeEvery)
function* watchWarehouseChange(action) {
  const panyId = 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${panyId}`, 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.

Share Improve this question asked Jun 2, 2017 at 16:59 Mathius17Mathius17 2,5022 gold badges24 silver badges34 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 11

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 parisons 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 ponent
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 ponent.

If you read the documentation of redux-saga and specifically call and put:

call:

fn: Function - A Generator function, or normal function which either returns a Promise as result, or any other value.

put:

Creates an Effect description that instructs the middleware to put an action into the provided channel.

Technically a thunk returns a Promise, which is why you can await a dispatch of a thunk:

export declare type AsyncThunkAction<Returned, ThunkArg, ThunkApiConfig extends AsyncThunkConfig> = (dispatch: GetDispatch<ThunkApiConfig>, getState: () => GetState<ThunkApiConfig>, extra: GetExtra<ThunkApiConfig>) => Promise<ReturnType<AsyncThunkFulfilledActionCreator<Returned, ThunkArg>> | ReturnType<AsyncThunkRejectedActionCreator<ThunkArg, ThunkApiConfig>>> & {
    abort(reason?: string): void;
    requestId: string;
    arg: ThunkArg;
};

This means you can do the following to dispatch a thunk from a saga:

yield put(
  yield call(syncItems)
);

The call method returns the redux action of your syncItems saga, which will be used by the put method to dispatch your action.

use https://github./czewail/bind-promise-to-dispatch package

add resolve and reject paramers in saga func

then use this package func wrap this.props.dispatch

then you can use it with promise

发布评论

评论列表(0)

  1. 暂无评论