如何让useEffect仅在多依赖均就绪时执行一次?
问题背景
当前使用的useEffect代码如下:
useEffect(() => { // 业务逻辑代码 }, [apiData, currentMeasurements]);
注:
apiData是通过 Apollo Client 的useLazyQuery获取的。
表单提交时,会同时触发API数据请求和执行setCurrentMeasurements([])更新状态。由于API调用存在延迟,React会先在currentMeasurements更新时执行一次useEffect(此时apiData还是旧值),等API响应返回后再执行第二次,导致页面先渲染旧数据,几秒后才显示新数据,不符合预期。
需要实现的效果:让useEffect仅在两个依赖项都就绪时执行,或者只在API响应返回后执行。曾尝试用setTimeout延迟更新currentMeasurements,但API响应延迟时会出现问题。
方案1:在useEffect内部添加就绪判断
直接在useEffect回调开头检查两个依赖的就绪状态,只有满足条件才执行后续逻辑。结合useLazyQuery返回的loading状态(判断API请求是否完成),可以精准控制执行时机:
const [getApiData, { loading, data: apiData }] = useLazyQuery(QUERY); useEffect(() => { // 仅当API请求完成、数据存在,且currentMeasurements已更新时执行 // 如果currentMeasurements允许为空数组,可把判断改为currentMeasurements !== undefined if (!loading && apiData && currentMeasurements) { // 这里写你的业务逻辑 } }, [apiData, currentMeasurements, loading]);
方案2:生成就绪标志,仅监听该标志
用useMemo生成一个“就绪状态标志”,只有当两个依赖都满足条件时标志才为true,让useEffect只监听这个标志:
const [getApiData, { loading, data: apiData }] = useLazyQuery(QUERY); // 生成就绪判断:API加载完成+数据存在,同时currentMeasurements已更新 const isAllReady = useMemo(() => { return !loading && apiData && currentMeasurements !== undefined; }, [loading, apiData, currentMeasurements]); useEffect(() => { if (isAllReady) { // 你的业务逻辑 } }, [isAllReady]);
这种方式能让useEffect的依赖列表更简洁,逻辑更清晰,适合依赖项较多的场景。
方案3:利用useLazyQuery的回调直接触发逻辑
既然apiData来自useLazyQuery,可以直接在API请求成功的onCompleted回调中处理业务逻辑,完全绕开useEffect的依赖触发问题:
// 用ref保存currentMeasurements的最新值,避免异步状态更新的问题 const currentMeasurementsRef = useRef(); useEffect(() => { currentMeasurementsRef.current = currentMeasurements; }, [currentMeasurements]); const [getApiData] = useLazyQuery(QUERY, { onCompleted: (data) => { // API响应返回后,直接使用最新的currentMeasurements值执行逻辑 const latestMeasurements = currentMeasurementsRef.current; if (latestMeasurements) { // 这里写你的业务逻辑,可直接用data和latestMeasurements } }, }); // 表单提交处理 const handleSubmit = () => { setCurrentMeasurements([]); getApiData(); // 触发API请求,成功后自动执行onCompleted中的逻辑 };
这种方式直接在API请求完成的时机执行逻辑,能确保拿到最新的currentMeasurements值,不会出现提前触发的问题。
内容的提问来源于stack exchange,提问作者KotlinOverflow

