乐趣区

关于javascript:React-hooks应该立即反映props的变化而不应等待useEffectuseLayoutEffect来纠正数据不一致

让咱们用具体例子来阐明。对于以下的useAsynchook,你能看出哪些问题?

function useAsync(id: number) {const [state, setState] = useState({
    loading: true,
    data: null as null | string
  });

  useEffect(() => {
    let discarded = false;
    setState({
      loading: true,
      data: null
    });
    fakeAPI(id).then((data) => {if (discarded) return;
      setState({
        loading: false,
        data
      });
    });

    return () => {discarded = true;};
  }, [id]);

  return state;
}

async function fakeAPI(id: number) {return new Promise<string>((resolve) => {setTimeout(() => {resolve(`data for ${id}`);
    }, 1000);
  });
}

查看残缺示例:

为了简化例子,咱们省略了错误处理

揭晓答案:在 id 变动当前的第一次渲染,useAsync仍会返回上一个 id 对应的 data。这一次渲染产生了两个 不统一 的数据:新的 id 和旧的 data。

很多人认为这不是一个问题,因为在这次渲染当前,useEffect会立刻更新状态为 loading(纠正不统一),而后下一次渲染就会返回 loading 的状态。

然而,这次谬误渲染 尽管放弃的工夫很短,然而依然会产生无奈意料的副作用

在咱们的残缺示例中,useAsync是这样被应用的:

const [currentId, setCurrentId] = useState(1);
const {data, loading} = useAsync(currentId);
useEffect(() => {if (!loading && data) {
    // will trigger side effect with inconsistent id and data
    console.log("Trigger some side effect!! id:", currentId, ", data:", data);
  }
}, [currentId, loading, data]);

其中一个打印输出就是 Trigger some side effect!! id: 2 , data: data for 1。阐明 某次副作用的执行用到了这两个不统一的数据,可能会造成无法挽回的影响。

能够看到,currentIddata 被同时用于一个副作用操作中。当这两个数据呈现不统一的时候,可能会造成无法挽回的影响。

从这个例子,咱们要汲取的教训是:

  • React hooks 应该 立刻 反映 props 的变动,而不应期待 useEffect/useLayoutEffect 来纠正数据不统一
  • 尽管谬误的渲染,放弃的工夫很短(会立刻在 useEffect/useLayoutEffect 中被纠正),然而当谬误渲染被 提交 当前,依然会触发意料之外的 副作用,结果可能是无法挽回的。这些副作用包含:

    • useEffectuseLayoutEffect定义的副作用,应用了不统一的数据
    • DOM 更新,将不统一的数据渲染到视图上。(当然,如果你立刻在 useLayoutEffect 中纠正了数据不统一,那么用户不会看到)
    • 在谬误的渲染中,初始化了一个状态,应用了不统一的数据,比方useState(() => currentId + data)。这个状态被谬误地初始化当前,如果没有及时手动纠正,会造成后续更多的谬误
    • 在谬误的渲染中,你批改了一个 ref 对象,应用了不统一的数据。比方ref.current = currentId + data。和上一条相似的情理,这样的数据如果没有及时手动纠正,会造成后续更多的谬误
退出移动版