React Native中如何等待Redux dispatch完成后再执行导航?
问题:如何等待Redux Saga的dispatch完成后执行导航?
在React Native组件中定义了onPressItem函数,需要先通过Redux dispatch FormActionLoadFormData动作,完成后再调用pushScreenOnFormScreen跳转页面。尝试用async/await修饰但报错'await has no effect on the type of this expression';用useEffect则导致导航函数多次执行,引发导航栈重复ID的警告/错误。
相关代码
组件中的onPressItem函数:
const onPressItem = async (formData: any) => { setFormData(formData); dispatch(FormActionLoadFormData(formData)); pushScreenOnFormScreen({ componentId: Screens.FORM_FIELDS_SCREEN, passProps: { formData: formData, formFields: formFields }, }); };
对应的Saga处理代码:
function* FormSagaLoadFormData({ payload, }: FormTypeLoadFormData): Generator<ReturnYield> { try { // Getting the auth token from store. const token: any = yield select(userSelectorAuthenticationToken); // Here the payload is just the { id: 00, name: '', ... }. // @ts-ignore const id: number = payload ? (payload.id ? payload.id : -1) : -1; const result: any = yield call(ApiGetFormByID, {token, id}); let form: any = null; try { form = result.data.data.form; } catch (err) { // } if (form) { yield put({ type: FORM_ACTION.REDUCER.SAVE_FORM_DATA, payload: form, }); } } catch (error) { console.log('FormSagaLoadFormData error', error); } }
解决方案
方法1:将导航逻辑移至Saga中执行
既然使用Redux Saga管理异步流程,最直接的方式是在Saga完成数据加载和store更新后,直接触发导航操作,确保流程顺序。
修改Saga代码,在成功保存form数据后调用导航:
function* FormSagaLoadFormData({ payload, }: FormTypeLoadFormData & { formFields?: any }): Generator<ReturnYield> { try { const token: any = yield select(userSelectorAuthenticationToken); const id: number = payload ? (payload.id ? payload.id : -1) : -1; const result: any = yield call(ApiGetFormByID, {token, id}); let form: any = null; try { form = result.data.data.form; } catch (err) { // 处理数据解析错误 } if (form) { yield put({ type: FORM_ACTION.REDUCER.SAVE_FORM_DATA, payload: form, }); // 数据更新完成后执行导航 pushScreenOnFormScreen({ componentId: Screens.FORM_FIELDS_SCREEN, passProps: { formData: form, formFields: payload.formFields }, }); } } catch (error) { console.log('FormSagaLoadFormData error', error); } }
同时修改组件中的onPressItem,将formFields传入action的payload:
const onPressItem = (formData: any) => { setFormData(formData); dispatch(FormActionLoadFormData({ ...formData, formFields: formFields })); };
方法2:封装返回Promise的dispatch函数
通过给action添加回调,让Saga在流程结束时触发回调,从而实现await等待。
首先在组件中封装自定义dispatch:
const dispatchWithPromise = (action) => { return new Promise((resolve) => { action.onSuccess = resolve; dispatch(action); }); };
修改Saga代码,接收并执行回调:
function* FormSagaLoadFormData({ payload, onSuccess, }: FormTypeLoadFormData & { onSuccess?: () => void }): Generator<ReturnYield> { try { const token: any = yield select(userSelectorAuthenticationToken); const id: number = payload ? (payload.id ? payload.id : -1) : -1; const result: any = yield call(ApiGetFormByID, {token, id}); let form: any = null; try { form = result.data.data.form; } catch (err) { // 处理解析错误 } if (form) { yield put({ type: FORM_ACTION.REDUCER.SAVE_FORM_DATA, payload: form, }); } // 流程结束后触发回调 onSuccess?.(); } catch (error) { console.log('FormSagaLoadFormData error', error); onSuccess?.(); // 错误场景也可触发,或改为reject } }
最后修改组件的onPressItem:
const onPressItem = async (formData: any) => { setFormData(formData); await dispatchWithPromise(FormActionLoadFormData(formData)); pushScreenOnFormScreen({ componentId: Screens.FORM_FIELDS_SCREEN, passProps: { formData: formData, formFields: formFields }, }); };
方法3:监听store状态变化,配合useRef控制导航执行
如果不想修改Saga,可以通过监听store中formData的更新,用useRef标记当前触发的操作,避免重复导航。
在组件中添加监听逻辑:
import { useSelector } from 'react-redux'; import { useRef, useEffect } from 'react'; // 获取store中保存的form数据 const storeFormData = useSelector(state => state.form.formData); const isPendingNavigation = useRef(false); const lastTriggeredFormId = useRef<number | null>(null); const onPressItem = (formData: any) => { setFormData(formData); lastTriggeredFormId.current = formData.id; isPendingNavigation.current = true; dispatch(FormActionLoadFormData(formData)); }; // 监听store中formData的变化 useEffect(() => { if (isPendingNavigation.current && storeFormData?.id === lastTriggeredFormId.current) { pushScreenOnFormScreen({ componentId: Screens.FORM_FIELDS_SCREEN, passProps: { formData: storeFormData, formFields: formFields }, }); // 重置标记,避免重复执行 isPendingNavigation.current = false; lastTriggeredFormId.current = null; } }, [storeFormData, formFields, pushScreenOnFormScreen]);
内容的提问来源于stack exchange,提问作者user3431310
相关产品推荐
相关产品推荐

