共计 1567 个字符,预计需要花费 4 分钟才能阅读完成。
让咱们用具体例子来阐明。对于以下的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
。和上一条相似的情理,这样的数据如果没有及时手动纠正,会造成后续更多的谬误
正文完
发表至: javascript
2021-03-11