说说竞态
下面是一个典型的在class组件里发请求的例子:
class Article extends Component {
state = {
article: null
};
componentDidMount() {
this.fetchData(this.props.id);
}
async fetchData(id) {
const article = await API.fetchArticle(id);
this.setState({ article });
}
// ...
}
你很可能已经知道,上面的代码埋伏了一些问题。它并没有处理更新的情况。所以第二个你能够在网上找到的经典例子是下面这样的:
class Article extends Component {
state = {
article: null
};
componentDidMount() {
this.fetchData(this.props.id);
}
componentDidUpdate(prevProps) { if (prevProps.id !== this.props.id) { this.fetchData(this.props.id); } } async fetchData(id) {
const article = await API.fetchArticle(id);
this.setState({ article });
}
// ...
}
这显然好多了!但依旧有问题。有问题的原因是请求结果返回的顺序不能保证一致。比如我先请求 {id: 10}
,然后更新到{id: 20}
,但{id: 20}
的请求更先返回。请求更早但返回更晚的情况会错误地覆盖状态值。
这被叫做竞态,这在混合了async
/ await
(假设在等待结果返回)和自顶向下数据流的代码中非常典型(props和state可能会在async函数调用过程中发生改变)。
Effects并没有神奇地解决这个问题,尽管它会警告你如果你直接传了一个async
函数给effect。(我们会改善这个警告来更好地解释你可能会遇到的这些问题。)
如果你使用的异步方式支持取消,那太棒了。你可以直接在清除函数中取消异步请求。
或者,最简单的权宜之计是用一个布尔值来跟踪它:
function Article({ id }) {
const [article, setArticle] = useState(null);
useEffect(() => {
let didCancel = false;
async function fetchData() {
const article = await API.fetchArticle(id);
if (!didCancel) { setArticle(article);
}
}
fetchData();
return () => { didCancel = true; }; }, [id]);
// ...
}
打个 比方 远程联想 我先输入张三,立马抹掉 输入李四,张三的接口 后返回了,这时现象就是我输得是李四 下拉框中 是 张三返回的数据
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。