在React的類組件時代,請求數據的時機常常放在componentDidMount中,而後state中須要有一個變量記錄當前是否正在請求接口,在請求的先後須要手動去改變這些狀態,大概代碼以下:react
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} /> ); } } 複製代碼
自從React發佈了Hook以來,這個組織代碼邏輯的方式廣受歡迎,在Hook時代咱們能夠把請求先後的loading狀態變量在自定義hook中管理起來,代碼示例:ios
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 };
};
複製代碼
// App.js
function App() {
const { loading, data } = useRequest(() => axios.get('/api/test'));
return loading ? '正在加載中...' : (
<Page data={data} /> ); } 複製代碼
React發佈了Suspense之後,數據請求又有了新思路,咱們能夠在視圖容器的外層包裹一層Suspense,在內部經過向外throw Promise的方式告知Suspense咱們的組件尚未準備好,須要展現Loading狀態。git
具體的代碼能夠看這裏:codesandbox.io/s/react-swr…github
// 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> ); } 複製代碼
// 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>
);
}
複製代碼
import useSWR from "swr";
import fetcher from "./fetcher";
export default url => {
return useSWR(url, fetcher, { suspense: true });
};
複製代碼
// 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模式axios
第二項參數所須要的fetcher就是本身定義的返回promise的邏輯。api
在這種Suspense模式下,咱們能夠輕鬆的實現Loading狀態的管理,並且不須要在Page組件中再去關心和聲明加載中的組件。promise
關於swr
這個庫的具體分析文章能夠查看這篇:精讀《Hooks 取數 - swr 源碼》緩存
這個Demo中在路由進入事後若是再次進入,數據會直接顯示以前請求過的,你會發現這很是像Vue中的keep-alive
帶來的效果,這是由於swr這個庫在suspense模式下默認作了數據的緩存,若是想要關掉它目前還沒在文檔中看到相應的配置。antd
// 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,其餘並無什麼改變。react-router
使用:
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 源碼》