React自定义Hook异步函数中useState更新未同步到父组件问题
问题原因与解决方案
核心问题
- React状态更新的异步性:
fetchRealEstates里的setRealEstates是异步操作,即便你用await等待请求完成,当前函数作用域内的realEstates还是旧的闭包值——React不会立刻更新状态,要等到下一次渲染才会更新变量引用。所以你在await fetchRealEstates之后直接dispatch(setRealEstates(realEstates)),实际dispatch的是旧数据。 - 状态重复维护:同时在自定义Hook和Redux里维护
realEstates状态,这种重复设计很容易导致数据不同步,完全没必要。
解决方案
方案1:去掉重复状态,直接使用Hook返回的数据
如果组件或其他依赖数据的模块可以直接从自定义Hook获取realEstates,直接删掉dispatch(setRealEstates(realEstates))这行代码即可,完全不需要同步到Redux。
方案2:必须同步到Redux时,用请求返回值更新Redux
修改自定义Hook的fetchRealEstates,让它返回请求到的最新数据,组件里直接拿到最新值dispatch:
自定义Hook修改后代码:
const [realEstates, setRealEstates] = useState<RealEstate[]>([]); ... const fetchRealEstates = useCallback(async (request: GetRealEstatesByCustomerNumberRequest): Promise<RealEstate[] | null> => { try { setError(null); setLoading(true); const response: GetRealEstatesByCustomerNumberResponse = await post('./api/Estate/get-real-estates-by-customer-number', request); const newRealEstates = response.RealEstates; setRealEstates(newRealEstates); return newRealEstates; // 返回最新数据 } catch (error: any) { setError(error); return null; } finally { setLoading(false); } }, []); // 注意:如果post函数不是稳定引用(比如在组件内定义),需要把它加到依赖数组里 ...
组件调用修改后代码:
const { realEstates, fetchRealEstates } = useRealEstate(); ... const onCustomerSearchChangeValue = async (customer: any) => { dispatch(setSelectedCustomerNumber(customer.value.ExternalClientNo)); const newRealEstates = await fetchRealEstates({ CustomerNumber: customer.value.ExternalClientNo }); if (newRealEstates) { dispatch(setRealEstates(newRealEstates)); } }; ...
方案3:用useEffect监听Hook状态变化自动同步
如果不想修改fetchRealEstates的返回值,可以在组件里用useEffect监听realEstates的变化,当Hook内的状态更新后自动同步到Redux:
const { realEstates, fetchRealEstates } = useRealEstate(); // 当realEstates变化时同步到Redux useEffect(() => { // 加判断避免初始空数组触发dispatch if (realEstates.length > 0) { dispatch(setRealEstates(realEstates)); } }, [realEstates, dispatch]); // 依赖要加全,避免闭包问题 const onCustomerSearchChangeValue = async (customer: any) => { dispatch(setSelectedCustomerNumber(customer.value.ExternalClientNo)); await fetchRealEstates({ CustomerNumber: customer.value.ExternalClientNo }); // 删掉原来的dispatch,交给useEffect处理 };
内容的提问来源于stack exchange,提问作者Umut Gür
相关产品推荐
相关产品推荐

