如何从 saga 发送 thunk?

How to dispatch a thunk from a saga?

我知道我不应该尝试从 sagas 中发送 thunk,这与 redux-saga 试图做的事情背道而驰。但我在一个相当大的应用程序中工作,大部分代码都是用 thunk 编写的,我们正在逐位迁移,需要从 saga 内部发送一个 thunk。 thunk 无法更改,因为它在其他部分使用(returns 承诺的 thunk),因此它会破坏很多东西。

配置商店:

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

传奇:

// 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
}

砰砰声:

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

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

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

每当执行 syncItems() 时,只有 first! 记录。 second! 永远不会发生。

PS:我没有收到任何错误或警告。

您使用的 syncItems 有误。关键是 syncItems 返回的 函数需要传递给 dispatch,而不是 syncItems 本身。正确的用法是:

yield put(syncItems());

我在我的博客 post Idiomatic Redux: Why use action creators? (based on an example gist I put together 中展示了一些值如何传递到 dispatch 的视觉比较。以下是示例:

// 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));

在您的情况下,只需将 yield put 替换为 this.props.dispatch,因为您是从 saga 而不是连接的组件进行调度。

使用https://github.com/czewail/bind-promise-to-dispatch

在 saga func 中添加 resolve 和 reject 参数

然后使用这个包 func wrap this.props.dispatch

然后你就可以使用 promise

如果您阅读了 redux-saga 的文档并明确调用和放置:

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.

从技术上讲,一个 thunk returns 一个 Promise,这就是为什么你可以 await 一个 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;
};

这意味着您可以执行以下操作从 saga 中发送 thunk:

yield put(
  yield call(syncItems)
);

call 方法 returns 您的 syncItems saga 的 redux actionput 方法将使用它来调度您的操作。