作者:Dmitri Pavlutin
譯者:小維FE
原文:dmitripavlutin.com
國外文章,筆者采用意譯的方式,以保證文章的可讀性,
當執行像資料獲取這樣的I/O操作時,你必須發起獲取請求,等待回應,將回應資料保存到組件的狀態中,最后渲染,異步的資料獲取會要求額外的作業來適應React的宣告性,React也在逐步改進去最小化這種額外的作業,生命周期函式,hooks和suspense都是React中獲取資料的方式,我將在下面的示例中分別討論它們的優缺點,只有了解每種方式的具體細節才能幫助你更好地在代碼中實作異步操作,
1. 使用生命周期函式獲取資料
下面這個應用必須要做兩件事情:
(1) 初始化獲取該公司的20位員工;
(2) 篩選出名稱包含查詢條件的員工,
在實作這些需求之前,回顧一下類組件的2個生命周期方法:
(1) componentDidMount():在組件掛載后執行一次;
(2) componentDidUpdate(prevProps):當props和state發生變化的時候執行,
<EmployeesPage>使用這兩個生命周期函式來實作了資料獲取邏輯:
import EmployeesList from "./EmployeesList";
import { fetchEmployees } from "./fake-fetch";
class EmployeesPage extends Component {
constructor(props) {
super(props);
this.state = { employees: [], isFetching: true };
}
componentDidMount() {
this.fetch();
}
componentDidUpdate(prevProps) {
if (prevProps.query !== this.props.query) {
this.fetch();
}
}
async fetch() {
this.setState({ isFetching: true });
const employees = await fetchEmployees(this.props.query);
this.setState({ employees, isFetching: false });
}
render() {
const { isFetching, employees } = this.state;
if (isFetching) {
return <div>Fetching employees....</div>;
}
return <EmployeesList employees={employees} />;
}
}
打開示例來探索一下<EmployeesPage>是如何來獲取資料的,<EmployeesPage>有一個異步的fetch方法用來獲取資料,當獲取完成的時候,使用獲取的employees來更新組件的狀態,this.fetch()在componentDidMount()生命周期函式中執行:當組件初始化渲染完成后開始獲取員工資料,
當用戶在這個輸入框中輸入查詢條件后,query屬性會發生變更,每次在它發生變化的時候,this.fetch()都將會在componentDidUpdate生命周期函式中執行,從而來實作員工篩選的功能,雖然生命周期方法相對容易掌握,但是基于類的方法存在樣板代碼和可重用性困難,
優點:
簡便,比較容易理解:生命周期方法componentDidMount在組件第一次初始化渲染后發起獲取資料的請求,當props發生變化的時候通過componentDidUpdate來重新獲取資料,
缺點:
(1) 樣板代碼,基于類的組件的代碼具有"儀式感":繼承React.Component,并且需要在constructor中呼叫super(props)等等;
(2) this問題,使用this關鍵字很麻煩;
(3) 代碼重復,在componentDidMount和componentDidUpdate中的代碼其實大部分都是重復的;
(4) 難以重用,獲取員工的邏輯如果是在其他的組件中就已經很難復用了,
2. 使用hooks獲取資料
對于基于類的組件來說,hooks是一個更好的選擇,作為一個普通函式來講,hooks摒棄了具有"儀式感"的代碼并且更容易復用,
我們首先來回顧一下useEffect(callback[, deps])鉤子函式的使用,這個鉤子函式在組件掛載后會執行callback,當依賴項deps發生變化后會繼續執行后續的渲染,在下面的示例中<EmployeesPage>使用useEffect()來獲取員工資料:
import React, { useState } from 'react';
import EmployeesList from "./EmployeesList";
import { fetchEmployees } from "./fake-fetch";
function EmployeesPage({ query }) {
const [isFetching, setFetching] = useState(false);
const [employees, setEmployees] = useState([]);
useEffect(function fetch() {
(async function() {
setFetching(true);
setEmployees(await fetchEmployees(query));
setFetching(false);
})();
}, [query]);
if (isFetching) {
return <div>Fetching employees....</div>;
}
return <EmployeesList employees={employees} />;
}
打開示例來看看useEffect()是如何來獲取資料的,你可以看到使用了useEffect()的版本要比類組件的版本簡化得多,在EmployeesPage函式式組件中useEffect(fetch, [query])在組件第一次渲染后執行fetch回呼,并且只有在query屬性發生變化后,組件才會在重渲染后再次執行fetch回呼,但是我們還有提升的空間,hooks允許你將獲取員工的邏輯從<EmployeesPage>中提取出來,我們來試試:
import React, { useState } from 'react';
import EmployeesList from "./EmployeesList";
import { fetchEmployees } from "./fake-fetch";
function useEmployeesFetch(query) {
const [isFetching, setFetching] = useState(false);
const [employees, setEmployees] = useState([]);
useEffect(function fetch {
(async function() {
setFetching(true);
setEmployees(await fetchEmployees(query));
setFetching(false);
})();
}, [query]);
return [isFetching, employees];
}
function EmployeesPage({ query }) {
const [employees, isFetching] = useEmployeesFetch(query);
if (isFetching) {
return <div>Fetching employees....</div>;
}
return <EmployeesList employees={employees} />;
}
我們的資料獲取的邏輯已經被提取到useEmployeesFetch()中了,組件<EmployeesPage>已經沒有集成任何資料獲取相關的邏輯,而是專注于他最直接的作業:UI渲染,更重要的是,你可以在任何需要獲取員工資料的組件中復用useEmployeesFetch(),
優點
(1) 簡單明了,hooks沒有樣板代碼的約束因為它們只是普通的函式;
(2) 復用性,hooks中所實作的資料獲取邏輯是很容易被復用的,
缺點
(1) 入門障礙,hooks有點違反常規,你在使用之前必須要理解他們,hooks依賴閉包,所以你也有必要將它們弄清楚,
(2) 命令式的,使用hooks,你仍舊需要使用命令式的方式來執行資料獲取,
3. 使用suspense獲取資料
suspense提供了宣告性的方式來在React中異步獲取資料,
<Suspense>包裝一個執行異步操作的組件:
<Suspense fallback={<span>Fetch in progress...</span>}>
<FetchSomething />
</Suspense>
當正在獲取資料的時候,suspense會渲染fallback屬性中的內容,當資料獲取完成后,suspense會使用獲取到的資料來渲染<FetchSomething />組件,我們來看看如何將suspense運用到員工應用中:
import React, { Suspense } from "react";
import EmployeesList from "./EmployeesList";
function EmployeesPage({ resource }) {
return (
<Suspense fallback={<h1>Fetching employees....</h1>}>
<EmployeesFetch resource={resource} />
</Suspense>
);
}
function EmployeesFetch({ resource }) {
const employees = resource.employees.read();
return <EmployeesList employees={employees} />;
}
打開示例來檢查suspense是如何作業的,<EmployeesPage>使用suspense來處理組件<EmployeesFetch>中的資料獲取邏輯,<EmployeesFetch> 中的resource.employees是一個特殊的wrapped promise用來在后臺和suspense通信,采用這種方式suspense可以知道在<EmployeesFetch>被渲染之前需要暫停多久,并且知道當資源就緒后,能繼續渲染,
最大的亮點:Suspense采用一種宣告性和同步的方式來處理異步操作,
這些組件本身并沒有集成任何資料獲取相關的細節,相反它們宣告性地使用resource來渲染內容,沒有生命周期函式,沒有hooks,沒有async/await,也沒有在組件內部的回呼:僅僅只是渲染資源,
優點:
(1) 宣告性的,suspense允許你在React中以宣告性的方式執行異步操作;
(2) 簡單明了,宣告性代碼使用起來更加簡單,組件本身并不關心資料獲取的細節;
(3) 與獲取邏輯的松散耦合,因為使用suspense的組件本身是不知道如何獲取資料的:使用REST又或是GraphQL,suspense設定了一個邊界用來保護資料獲取的細節泄露到組件內部;
(4) 沒有競態條件,如果同時開啟了多個異步獲取的操作,suspense會使用最近一次的獲取請求,
缺點:
需要配接器,suspense需要已經實作了其fetching介面的專業的fetching庫或者配接器,
4. 總結
生命周期函式曾經很長一段時間是資料獲取的唯一手段,然而通過這種方式會帶來很多問題比如大量的樣板代碼,重復和復用難度,而使用hooks來獲取資料是一個更好的選擇:我們減少了很多樣板代碼,suspense的好處是宣告性獲取資料,你的組件本身并不關心資料獲取的細節,同時suspense是最接近React本身的宣告性理念的,
你更喜歡哪種獲取資料的方式呢?
原文: https://dmitripavlutin.com/react-fetch-lifecycle-methods-hooks-suspense/
5. 交流
今天主要分享了React中幾種資料獲取的方式以及每種方式的優缺點對比,旨在讓大家在異步操作中能夠更得心應手,希望能和大家相互討論技術,一起交流學習,
文章已同步更新至Github博客,若覺文章尚可,歡迎前往star!
你的一個點贊,值得讓我付出更多的努力!
逆境中成長,只有不斷地學習,才能成為更好的自己,與君共勉!

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/175416.html
標籤:JavaScript
上一篇:ECMA6新增語法(待續...)
下一篇:js 時間常用處理方法
