让咱们用具体例子来阐明。对于以下的useAsync
hook,你能看出哪些问题?
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
。阐明某次副作用的执行用到了这两个不统一的数据,可能会造成无法挽回的影响。
能够看到,currentId
和data
被同时用于一个副作用操作中。当这两个数据呈现不统一的时候,可能会造成无法挽回的影响。
从这个例子,咱们要汲取的教训是:
- React hooks应该立刻反映props的变动,而不应期待useEffect/useLayoutEffect来纠正数据不统一
尽管谬误的渲染,放弃的工夫很短(会立刻在useEffect/useLayoutEffect中被纠正),然而当谬误渲染被提交当前,依然会触发意料之外的副作用,结果可能是无法挽回的。这些副作用包含:
useEffect
、useLayoutEffect
定义的副作用,应用了不统一的数据- DOM更新,将不统一的数据渲染到视图上。(当然,如果你立刻在useLayoutEffect中纠正了数据不统一,那么用户不会看到)
- 在谬误的渲染中,初始化了一个状态,应用了不统一的数据,比方
useState(() => currentId + data)
。这个状态被谬误地初始化当前,如果没有及时手动纠正,会造成后续更多的谬误 - 在谬误的渲染中,你批改了一个ref对象,应用了不统一的数据。比方
ref.current = currentId + data
。和上一条相似的情理,这样的数据如果没有及时手动纠正,会造成后续更多的谬误