如何从 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 的文档并明确调用和放置:
fn: Function - A Generator function, or normal function which either returns a Promise as result, or any other value.
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 action
,put
方法将使用它来调度您的操作。
我知道我不应该尝试从 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 的文档并明确调用和放置:
fn: Function - A Generator function, or normal function which either returns a Promise as result, or any other value.
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 action
,put
方法将使用它来调度您的操作。