React-Native中更新Redux后触发数据上传的规范实现方法
你遇到的核心问题是闭包捕获的旧值:useCaseUpload里通过useAppSelector获取的caseObject是Hook初始化时的状态快照,即使你同步dispatch了updateCase更新Redux状态,uploadAllCaseData里的caseObject仍然是闭包中的旧值,导致上传用的不是最新数据。
普通Redux同步action是同步执行的,但组件的状态更新是异步渲染的——dispatch完成后,Redux状态确实更新了,但组件需要等到下一次渲染才能拿到新的selector值,而当前onSave函数里的uploadAllCaseData引用的还是旧的闭包变量。await普通action毫无意义,因为dispatch同步action本身就是同步完成的,它不会返回Promise(除非你用了异步中间件,但普通action只是纯对象)。
以下是几种符合Redux范式的优雅解决方法:
方案一:直接传递最新数据(最简洁)
既然你已经在onSave里生成了updatedCase,完全可以直接把这个最新对象传给上传方法,不需要依赖Redux的selector获取,从根源避免闭包问题。
修改useCaseUpload,让上传方法接受参数:
const useCaseUpload = () => { const dispatch = useDispatch(); const uploadAllCaseData = async (caseObject: CaseType, observation: ObservationType) => { await dispatch(caseActions.uploadCase(caseObject)).unwrap(); await dispatch(caseObservationActions.uploadObservations(observation)).unwrap(); }; return { uploadAllCaseData }; };
然后在组件中调用时直接传入最新数据:
const AddNarrative = (props: IProps) => { const caseId = props.route.params.caseId; const caseObject = useAppSelector(state => state.case.cases.find(i => i.id === caseId))!; const observation = useAppSelector(state => state.caseObservations.observationsItems.find(item => item.caseId === caseId)); const [narrative, setNarrative] = useState(caseObject.narrative); const { uploadAllCaseData } = useCaseUpload(); const onSave = () => { const updatedCase = { ...caseObject, narrative }; dispatch(caseActions.updateCase(updatedCase)); // 直接传刚生成的最新对象 uploadAllCaseData(updatedCase, observation); }; };
方案二:在上传时直接从Store获取最新状态
如果必须依赖Redux状态(比如后续还有其他地方可能修改该数据),可以用useStore直接获取当前最新的Store状态,而不是依赖useAppSelector的闭包值:
import { useStore } from 'react-redux'; const useCaseUpload = (caseId: string) => { const dispatch = useDispatch(); const store = useStore(); const uploadAllCaseData = async () => { // 上传时实时获取最新状态 const state = store.getState(); const latestCase = state.case.cases.find(i => i.id === caseId)!; const latestObservation = state.caseObservations.observationsItems.find(item => item.caseId === caseId); await dispatch(caseActions.uploadCase(latestCase)).unwrap(); await dispatch(caseObservationActions.uploadObservations(latestObservation)).unwrap(); }; return { uploadAllCaseData }; };
这样不管useCaseUpload什么时候初始化,只要调用uploadAllCaseData,都会拿到当前Store的最新数据,彻底避开闭包陷阱。
方案三:用状态触发上传(适合复杂依赖场景)
如果需要等待组件渲染完成后再上传,可以通过一个触发状态结合useEffect实现:
const AddNarrative = (props: IProps) => { const caseId = props.route.params.caseId; const caseObject = useAppSelector(state => state.case.cases.find(i => i.id === caseId))!; const [narrative, setNarrative] = useState(caseObject.narrative); const [shouldUpload, setShouldUpload] = useState(false); const { uploadAllCaseData } = useCaseUpload(caseId); // 监听触发状态和最新的caseObject,状态变化时执行上传 useEffect(() => { if (shouldUpload) { uploadAllCaseData(); setShouldUpload(false); } }, [shouldUpload, caseObject, uploadAllCaseData]); const onSave = () => { const updatedCase = { ...caseObject, narrative }; dispatch(caseActions.updateCase(updatedCase)); setShouldUpload(true); }; };
这种方式利用React的生命周期,确保上传时已经拿到了更新后的Redux状态,但需要维护一个额外的触发状态,适合有多个依赖项需要同步的场景。
优先推荐方案一,逻辑最直接,代码量最少;如果必须依赖Redux状态,方案二更简洁;方案三适合复杂的状态依赖场景。
内容的提问来源于stack exchange,提问作者mark

