React Suspense + 自定義Hook開啓數據請求新方式。

過去

類組件

在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} /> ); } } 複製代碼

hook組件

自從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} /> ); } 複製代碼

將來

Suspense組件 + useSWR

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

本身實現簡易的useSWR

// 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,其最終的目的其實仍是讓咱們寫的代碼更加易於閱讀和維護,讓開發者越爽越好。

HookSuspense碰撞在一塊兒,讓組件內部的邏輯和請求、等待內部的狀態完全解耦開來了,相比之前的類組件,代碼變的愈來愈精簡。

期待React團隊的進一步動做吧!

參考文章:
React Concurrent 模式搶先預覽上篇: Suspense the world
精讀《Hooks 取數 - swr 源碼》

相關文章
相關標籤/搜索