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