重用saga逻辑
Reuse saga logic
我们有一个通用的 Form 组件,附带一个 saga 来处理验证和提交
function* validateAndSubmit(action) {
const errors = clientSideValidate(action.values);
if (errors) {
return yield put({type: SUBMIT_FAILED, formKey: action.formKey, errors: errors});
}
try {
const response = yield call(submitToTargetUrl(action.values, action.url));
if (response.errors) {
return yield put({type: SUBMIT_FAILED, formKey: action.formKey, errors: response.errors});
}
yield put({type: SUBMIT_SUCCESS, formKey: action.formKey});
} catch (e) {
yield put({type: SUBMIT_FAILED, formKey: action.formKey, errors: [e.message]});
}
}
function* form() {
yield takeEvery(SUBMITTED, validateAndSubmit);
}
现在,我们有另一个组件,比如说 UserForm
,它包装了一般的 Form 组件。在提交时,我们希望将表单提交到后端并同时从外部 API 获取一些数据,等待两者完成,然后调度一些操作。这个逻辑将存在于其他文件中的另一个 Saga 中。重用 validateAndSubmit
逻辑的正确模式是什么?有什么办法可以做到这一点:
function* handleUserFormSubmit(action) {
const [submitResult, fetchResult] = yield all([
call(validateAndSubmitSaga),
call(fetchOtherData),
]);
// ...test for successful results for both
if (allIsGood) {
yield put({type: ALL_IS_GOOD});
}
}
function* userForm() {
yield takeEvery(USER_FORM_SUBMITTED, handleUserFormSubmit);
}
谢谢!
我建议创建一个可重用的 validateAndSubmit
函数来处理验证和提交,然后 return 如果有任何错误。然后,有一个使用这个函数的表单提交传奇效果。
async function reusableValidateAndSubmit(formValues, submitUrl) {
try {
const errors = clientSideValidate(formValues);
if (errors) {
return errors;
}
const response = await submitToTargetUrl(formValues, submitUrl);
if (response.errors) {
return response.errors;
}
return null;
} catch (e) {
console.error('@reusableValidateAndSubmit: ', e);
return [e.message];
}
}
function* handleFormSubmitSaga(action) {
try {
const { values, url, formKey } = action;
const errors = yield call(reusableValidateAndSubmit, values, url);
if (errors) {
return yield put({type: SUBMIT_FAILED, formKey: formKey, errors: errors});
}
return yield put({type: SUBMIT_SUCCESS, formKey: formKey});
} catch (e) {
return yield put({type: SUBMIT_FAILED, formKey: action.formKey, errors: [e.message]});
}
}
function* form() {
yield takeEvery(SUBMITTED, handleFormSubmitSaga);
}
对于 handleUserFormSubmit
,我不太确定在您的用例中,如果 fetchOtherData
失败,您是否希望 validateAndSubmitSaga
失败,反之亦然。使用 redux-saga 的 all() 会带来这种效果,因为它的行为类似于 Promise.all()。
来自 MDN 的 Promise.all()
的 return 值的片段:
This returned promise is then resolved/rejected asynchronously (as soon as the stack is empty) when all the promises in the given iterable have resolved, or if any of the promises reject.
据推测,这是您预期的行为,并且已经实现了上面的代码。您可以重复使用 reusableValidateAndSubmit
函数
function* handleUserFormSubmit(action) {
const [submitError, fetchResult] = yield all([
call(reusableValidateAndSubmit, action.values, action.url),
call(fetchOtherData),
]);
// ...test for successful results for both
// submitError is null if submit was a success
// fetchResult must have a value or return true if was a success
if (!submitError && fetchResult) {
yield put({type: ALL_IS_GOOD});
}
}
我还可以建议您查看一些可以与 redux 合作的表单框架(即 redux-form),因为它们在某些用例中也有帮助。
我们最终得到了一个略有不同的解决方案。而不是 call
-ing 我们是 take
-ing:
function* handleUserFormSubmit(action) {
const [submitResult, fetchResult] = yield all([
yield take(SUBMIT_SUCCESS),
yield take(FETCH_OTHER_DATA_SUCCESS),
]);
// ...test for successful results for both
if (allIsGood) {
yield put({type: ALL_IS_GOOD});
}
}
function* userForm() {
yield takeEvery(USER_FORM_SUBMITTED, handleUserFormSubmit);
}
这样其他 Saga 可以不受干扰地做它的事情,这个 saga 可以根据自己的逻辑反应
我们有一个通用的 Form 组件,附带一个 saga 来处理验证和提交
function* validateAndSubmit(action) {
const errors = clientSideValidate(action.values);
if (errors) {
return yield put({type: SUBMIT_FAILED, formKey: action.formKey, errors: errors});
}
try {
const response = yield call(submitToTargetUrl(action.values, action.url));
if (response.errors) {
return yield put({type: SUBMIT_FAILED, formKey: action.formKey, errors: response.errors});
}
yield put({type: SUBMIT_SUCCESS, formKey: action.formKey});
} catch (e) {
yield put({type: SUBMIT_FAILED, formKey: action.formKey, errors: [e.message]});
}
}
function* form() {
yield takeEvery(SUBMITTED, validateAndSubmit);
}
现在,我们有另一个组件,比如说 UserForm
,它包装了一般的 Form 组件。在提交时,我们希望将表单提交到后端并同时从外部 API 获取一些数据,等待两者完成,然后调度一些操作。这个逻辑将存在于其他文件中的另一个 Saga 中。重用 validateAndSubmit
逻辑的正确模式是什么?有什么办法可以做到这一点:
function* handleUserFormSubmit(action) {
const [submitResult, fetchResult] = yield all([
call(validateAndSubmitSaga),
call(fetchOtherData),
]);
// ...test for successful results for both
if (allIsGood) {
yield put({type: ALL_IS_GOOD});
}
}
function* userForm() {
yield takeEvery(USER_FORM_SUBMITTED, handleUserFormSubmit);
}
谢谢!
我建议创建一个可重用的 validateAndSubmit
函数来处理验证和提交,然后 return 如果有任何错误。然后,有一个使用这个函数的表单提交传奇效果。
async function reusableValidateAndSubmit(formValues, submitUrl) {
try {
const errors = clientSideValidate(formValues);
if (errors) {
return errors;
}
const response = await submitToTargetUrl(formValues, submitUrl);
if (response.errors) {
return response.errors;
}
return null;
} catch (e) {
console.error('@reusableValidateAndSubmit: ', e);
return [e.message];
}
}
function* handleFormSubmitSaga(action) {
try {
const { values, url, formKey } = action;
const errors = yield call(reusableValidateAndSubmit, values, url);
if (errors) {
return yield put({type: SUBMIT_FAILED, formKey: formKey, errors: errors});
}
return yield put({type: SUBMIT_SUCCESS, formKey: formKey});
} catch (e) {
return yield put({type: SUBMIT_FAILED, formKey: action.formKey, errors: [e.message]});
}
}
function* form() {
yield takeEvery(SUBMITTED, handleFormSubmitSaga);
}
对于 handleUserFormSubmit
,我不太确定在您的用例中,如果 fetchOtherData
失败,您是否希望 validateAndSubmitSaga
失败,反之亦然。使用 redux-saga 的 all() 会带来这种效果,因为它的行为类似于 Promise.all()。
来自 MDN 的 Promise.all()
的 return 值的片段:
This returned promise is then resolved/rejected asynchronously (as soon as the stack is empty) when all the promises in the given iterable have resolved, or if any of the promises reject.
据推测,这是您预期的行为,并且已经实现了上面的代码。您可以重复使用 reusableValidateAndSubmit
函数
function* handleUserFormSubmit(action) {
const [submitError, fetchResult] = yield all([
call(reusableValidateAndSubmit, action.values, action.url),
call(fetchOtherData),
]);
// ...test for successful results for both
// submitError is null if submit was a success
// fetchResult must have a value or return true if was a success
if (!submitError && fetchResult) {
yield put({type: ALL_IS_GOOD});
}
}
我还可以建议您查看一些可以与 redux 合作的表单框架(即 redux-form),因为它们在某些用例中也有帮助。
我们最终得到了一个略有不同的解决方案。而不是 call
-ing 我们是 take
-ing:
function* handleUserFormSubmit(action) {
const [submitResult, fetchResult] = yield all([
yield take(SUBMIT_SUCCESS),
yield take(FETCH_OTHER_DATA_SUCCESS),
]);
// ...test for successful results for both
if (allIsGood) {
yield put({type: ALL_IS_GOOD});
}
}
function* userForm() {
yield takeEvery(USER_FORM_SUBMITTED, handleUserFormSubmit);
}
这样其他 Saga 可以不受干扰地做它的事情,这个 saga 可以根据自己的逻辑反应