过去
类组件
在React的类组件时代,请求数据的时机经常放在componentDidMount中,然后state中需要有一个变量记录当前是否正在请求接口,在请求的前后需要手动去改变这些状态,大概代码如下:
代码语言:javascript复制class App extends Component {
state = {
loading: false,
}
componentDidMount() {
this.setState({
data: null,
loading: true,
});
axios.get('/api/test').then((data) => {
this.setState({
data,
loading: false,
});
});
}
render() {
return this.state.loading ? '正在加载中...' : (
<Page data={data} />
);
}
}
hook组件
自从React发布了Hook以来,这个组织代码逻辑的方式广受欢迎,在Hook时代我们可以把请求前后的loading状态变量在自定义hook中管理起来,代码示例:
代码语言:javascript复制const useRequest = (fn, dependencies = []) => {
const [data, setData] = useState(defaultValue);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
fn()
.then(res => {
setData(res);
})
.finally(() => {
setLoading(false);
});
}, dependencies);
return { data, setData, loading };
};
代码语言:javascript复制// App.js
function App() {
const { loading, data } = useRequest(() => axios.get('/api/test'));
return loading ? '正在加载中...' : (
<Page data={data} />
);
}
未来
Suspense组件 useSWR
React发布了Suspense以后,数据请求又有了新思路,我们可以在视图容器的外层包裹一层Suspense,在内部通过向外throw Promise的方式告知Suspense我们的组件还没有准备好,需要展示Loading状态。
具体的代码可以看这里:codesandbox.io/s/react-swr…
代码语言:javascript复制// Router.js
import React, { Suspense } from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import { Spin } from "antd";
import Author from "./Pages/Author";
import Table from "./Pages/Table";
import Layout from "./Layout";
export default function App() {
return (
<Router>
<Layout>
<Suspense fallback={<Spin tip="正在拼命获取数据,请稍后..." />}>
<Switch>
<Route exact path="/">
<Author />
</Route>
<Route exact path="/table">
<Table />
</Route>
</Switch>
</Suspense>
</Layout>
</Router>
);
}
代码语言:javascript复制// pages/Author
import React from "react";
import useSWR from "../use-swr";
export default function Author() {
const { data } = useSWR("/api/user");
return (
<div>
<span>Hello {data.userName}</span>
</div>
);
}
代码语言:javascript复制import useSWR from "swr";
import fetcher from "./fetcher";
export default url => {
return useSWR(url, fetcher, { suspense: true });
};
代码语言:javascript复制// fetcher
const fetcher = url => {
let responseData;
switch (url) {
case "/api/user":
responseData = {
userName: "ssh"
};
break;
default:
break;
}
return new Promise(resolve => {
setTimeout(() => {
resolve(responseData);
}, 2000);
});
};
export default fetcher;
其实这个Demo中就是使用了swr这个库,对配置项进行了一个简单的封装,开启了suspense模式
第二项参数所需要的fetcher就是自己定义的返回promise的逻辑。
在这种Suspense模式下,我们可以轻松的实现Loading状态的管理,而且不需要在Page组件中再去关心和声明加载中的组件。
关于swr
这个库的具体分析文章可以查看这篇:精读《Hooks 取数 - swr 源码》
这个Demo中在路由进入过后如果再次进入,数据会直接显示之前请求过的,你会发现这非常像Vue中的keep-alive
带来的效果,这是因为swr这个库在suspense模式下默认做了数据的缓存,如果想要关掉它目前还没在文档中看到相应的配置。
自己实现简易的useSWR
代码语言:javascript复制// use-my-swr
import { useState, useEffect } from "react";
export default (url, fetcher) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
fetcher(url)
.then(result => {
setData(result);
})
.finally(() => {
setLoading(false);
});
}, [url, fetcher]);
if (loading) {
throw Promise.resolve(null);
} else {
return { data };
}
};
其实和上面写的useRequest相比,就是在loading的时候向外抛出一个promise,其他并没有什么改变。
使用:
代码语言:javascript复制import React from "react";
import useSWR from "../use-my-swr";
import fetcher from "../fetcher";
export default function Author() {
const { data } = useSWR("/api/user", fetcher);
return (
<div>
<span>Hello {data && data.userName}</span>
</div>
);
}
小结
这篇文章只是在Suspense到来之前的一点开胃前菜,在React的发展长河中,开发者经历了各种各样的写法,HOC、render-props、hook,其最终的目的其实还是让我们写的代码更加易于阅读和维护,让开发者越爽越好。
Hook
和Suspense
碰撞在一起,让组件内部的逻辑和请求、等待内部的状态彻底解耦开来了,相比以前的类组件,代码变的越来越精简。
期待React团队的进一步动作吧!
参考文章: React Concurrent 模式抢先预览上篇: Suspense the world 精读《Hooks 取数 - swr 源码》