在React中处理API请求并行 race http/fetch/axios/ajax 的情况
实际业务中不可避免的有网络请求的情况需要处理
开局
场景
假设一个有网络请求的组件,在页面切换的情况下,导致多次请求
list → detail/1
list → detail/2
list → detail/3
网络延时:有可能因为网络延时,出错等场景,我们在 detail/3 的时候,会闪现 detail/2 的数据
网各出错:有可能停留在上一个页面的数据,我们页面已经切到了 detail/3 但数据还在 detail/1 这里
先看2种错误的示范
const StarwarsHero = ({ id }) => {
const [data, setData] = useState(null);
useEffect(() => {
setData(null);
fetchStarwarsHeroData(id).then(
result => setData(result),
e => console.warn('fetch failure', e),
);
}, [id]);
return <div>{data ? data.name : <Spinner />}</div>;
};
class StarwarsHero extends React.Component {
state = { data: null };
fetchData = id => {
fetchStarwarsHeroData(id).then(
result => setState({ data: result }),
e => console.warn('fetch failure', e),
);
};
componentDidMount() {
this.fetchData(this.props.id);
}
componentDidUpdate(prevProps) {
if (prevProps.id !== this.props.id) {
this.fetchData(this.props.id);
}
}
render() {
const { data } = this.state;
return <div>{data ? data.name : <Spinner />}</div>;
}
}
分析问题
- useEffect 的时候,需要 abort 请求,否则会有异步带来的一系列问题
- 当 aborted 之后,后面的 setData 不要再继续执行了,否则也有可能会有错误情况
正确方案
useEffect(() => {
setData(null);
// Create the current request's abort controller
const abortController = new AbortController();
// Issue the request
fetchStarwarsHeroData(id, {
signal: abortController.signal,
})
// Simulate some delay/errors
.then(async data => {
await delayRandomly();
throwRandomly();
return data;
})
// Set the result, if not aborted
.then(
result => {
// IMPORTANT: we still need to filter the results here,
// in case abortion happens during the delay.
// In real apps, abortion could happen when you are parsing the json,
// with code like "fetch().then(res => res.json())"
// but also any other async then() you execute after the fetch
if (abortController.signal.aborted) {
return;
}
setData(result);
},
e => console.warn('fetch failure', e),
);
// Trigger the abortion in useEffect's cleanup function
return () => {
abortController.abort();
};
}, [id]);