传奇总是被取消
Saga always cancelled
我使用 redux-saga 并有以下代码:
function* loginFlow(username, password) {
try {
yield call(loginApi, username, password);
yield put({ type: LOGIN_SUCCESS });
yield put({ type: TOGGLE_LOGGED_DONE, payload: true });
yield put(push('/dashboard'));
} catch (error) {
yield put({ type: LOGIN_ERROR, error });
} finally {
if (yield cancelled()) {
console.log('ALWAYS CANCELLED');
// yield put(replace('/login'));
}
}
}
// Watcher saga.
function* loginWatcher() {
while (true) {
const { username, password } = yield take(LOGIN_REQUESTING);
const task = yield fork(loginFlow, username, password);
const action = yield take([LOGOUT, LOGIN_ERROR]);
if (action.type === LOGOUT) yield cancel(task);
yield call(logoutUser);
}
}
问题是 loginFlow
函数最后总是被取消(我在控制台中看到 'ALWAYS CANCELLED'
)。即使我从 loginWatcher
.
中删除 const action = yield take([LOGOUT, LOGIN_ERROR]);
和 yield call(logoutUser);
我看不到 LOGOUT
或 LOGIN_ERROR
被解雇了:
知道我的代码有什么问题吗?
请注意,我在 index.js 中使用 withRouter
作为上面的 saga 所在的登录页面(否则我在重定向时会出现空白屏幕):
const withSaga = injectSaga({ key: 'login', saga });
export default compose( withReducer, withSaga, withConnect, )(LoginPage);
此致
编辑:
如果在 try/catch
中包装 loginWatcher
我也会去 finally
那里:
function* loginWatcher() {
while (true) {
try {
const { username, password } = yield take(LOGIN_REQUESTING);
const task = yield fork(loginFlow, username, password);
const action = yield take([LOGOUT, LOGIN_ERROR]);
if (action.type === LOGOUT) yield cancel(task);
yield call(logoutUser);
} catch (error) {
yield put({ type: LOGIN_ERROR, error });
} finally {
if (yield cancelled()) {
console.log('HERE AS WELL');
// yield put(push('/login'));
}
}
}
}
我发布了我们收集的答案,以便这个问题对未来的读者有用。
在 redux-saga 中,通过 call()
和 fork()
启动的传奇被递归取消。 Saga表示递归可取消任务。所以当一个 saga 被取消时,你应该检查它的父 saga,等等。
您发布的这段代码:
const withSaga = injectSaga({ key: 'login', saga });
export default compose( withReducer, withSaga, withConnect, )(LoginPage)
Google建议使用this redux-saga + react-boilerplate example。它涵盖取消以及如何禁用它。查看 link 了解相关信息。
我不熟悉 react-boilerplate(React 生态系统很大),但我想我的回答至少缩小了搜索范围。如果您感到困惑,请考虑使用 react-boilerplate 提出一个单独的问题。
我使用 redux-saga 并有以下代码:
function* loginFlow(username, password) {
try {
yield call(loginApi, username, password);
yield put({ type: LOGIN_SUCCESS });
yield put({ type: TOGGLE_LOGGED_DONE, payload: true });
yield put(push('/dashboard'));
} catch (error) {
yield put({ type: LOGIN_ERROR, error });
} finally {
if (yield cancelled()) {
console.log('ALWAYS CANCELLED');
// yield put(replace('/login'));
}
}
}
// Watcher saga.
function* loginWatcher() {
while (true) {
const { username, password } = yield take(LOGIN_REQUESTING);
const task = yield fork(loginFlow, username, password);
const action = yield take([LOGOUT, LOGIN_ERROR]);
if (action.type === LOGOUT) yield cancel(task);
yield call(logoutUser);
}
}
问题是 loginFlow
函数最后总是被取消(我在控制台中看到 'ALWAYS CANCELLED'
)。即使我从 loginWatcher
.
const action = yield take([LOGOUT, LOGIN_ERROR]);
和 yield call(logoutUser);
我看不到 LOGOUT
或 LOGIN_ERROR
被解雇了:
知道我的代码有什么问题吗?
请注意,我在 index.js 中使用 withRouter
作为上面的 saga 所在的登录页面(否则我在重定向时会出现空白屏幕):
const withSaga = injectSaga({ key: 'login', saga });
export default compose( withReducer, withSaga, withConnect, )(LoginPage);
此致
编辑:
如果在 try/catch
中包装 loginWatcher
我也会去 finally
那里:
function* loginWatcher() {
while (true) {
try {
const { username, password } = yield take(LOGIN_REQUESTING);
const task = yield fork(loginFlow, username, password);
const action = yield take([LOGOUT, LOGIN_ERROR]);
if (action.type === LOGOUT) yield cancel(task);
yield call(logoutUser);
} catch (error) {
yield put({ type: LOGIN_ERROR, error });
} finally {
if (yield cancelled()) {
console.log('HERE AS WELL');
// yield put(push('/login'));
}
}
}
}
我发布了我们收集的答案,以便这个问题对未来的读者有用。
在 redux-saga 中,通过 call()
和 fork()
启动的传奇被递归取消。 Saga表示递归可取消任务。所以当一个 saga 被取消时,你应该检查它的父 saga,等等。
您发布的这段代码:
const withSaga = injectSaga({ key: 'login', saga });
export default compose( withReducer, withSaga, withConnect, )(LoginPage)
Google建议使用this redux-saga + react-boilerplate example。它涵盖取消以及如何禁用它。查看 link 了解相关信息。
我不熟悉 react-boilerplate(React 生态系统很大),但我想我的回答至少缩小了搜索范围。如果您感到困惑,请考虑使用 react-boilerplate 提出一个单独的问题。