1.开发环境下组件加载两次?
非bug,重新安装组件仅在开发过程中发生,帮助找到需要清理的效果。在生产环境中只会加载一次。
- React 将在 Effect 下次运行之前以及卸载期间调用您的清理函数。
return () => {};
2. 🌰订阅事件情况,清除订阅:
useEffect(() => {
function handleScroll(e) {
console.log(window.scrollX, window.scrollY);
}
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
在开发中, Effect call addEventListener()
,然后立即callremoveEventListener()
,然后再次calladdEventListener()
。因此一次只有一个活动订阅。这与在生产中调用一次addEventListener()
具有相同的用户可见行为。
3. 🌰接口获取,异步操作“竞争”,清除事件:
如果快速切换select选项,会造成fetchBio会同时请求多个,但是可能以意想不到的顺序到达
原代码:
import { useState, useEffect } from 'react';
import { fetchBio } from './api.js';
export default function Page() {
const [person, setPerson] = useState('Alice');
const [bio, setBio] = useState(null);
useEffect(() => {
setBio(null);
fetchBio(person).then(result => {
setBio(result);
});
}, [person]);
return (
<>
<select value={person} onChange={e => {
setPerson(e.target.value);
}}>
<option value="Alice">Alice</option>
<option value="Bob">Bob</option>
<option value="Taylor">Taylor</option>
</select>
<hr />
<p><i>{bio ?? 'Loading...'}</i></p>
</>
);
}
select框依次点击顺序:alice->bob->taylor
发现执行出来是错误结果,taylor选项下对应的页面中竟然显示的是“bob”
修改代码:
useEffect(() => {
let ignore = false;
setBio(null);
fetchBio(person).then(result => {
if (!ignore) {
setBio(result);
}
});
return () => {
ignore = true;
}
}, [person]);
select框依次点击顺序:alice->bob->taylor,结果显示正确
解析
每个渲染器的 Effect 都有自己的ignore
变量。最初,ignore
变量设置为false
。
但是,如果 Effect 被清除(例如当您选择不同的人时),它的ignore
变量将变为true
。
请求完成的顺序并不重要。
只有最后一个人的 Effect 会ignore
设置为false
,所以它会调用setBio(result)
。过去的影响已被清除,因此if (!ignore)检查将阻止他们调用setBio:
- 选择’Bob’触发器
fetchBio('Bob')
- 选择’Taylor’触发器
fetchBio('Taylor')
并清除之前的(Bob 的)效果- 抓取’Taylor’完成前抓取’Bob’
- 'Taylor’渲染调用的效果
setBio('This is Taylor’s bio')
- 抓取’Bob’完成
- 'Bob’渲染的效果不做任何事情,因为它的
ignore
标志被设置为true