让咱们用具体例子来阐明。对于以下的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。和上一条相似的情理,这样的数据如果没有及时手动纠正,会造成后续更多的谬误