摘要:現(xiàn)在,請(qǐng)求數(shù)據(jù)和查詢(xún)參數(shù)兩個(gè)相互獨(dú)立,但是我們需要像一個(gè)辦法希望他們耦合起來(lái),只獲取輸入框輸入的參數(shù)指定的話題文章。好了,現(xiàn)在一旦你改變輸入框內(nèi)容,數(shù)據(jù)就會(huì)重新獲取。
通過(guò)這個(gè)教程,我想告訴你在 React 中如何使用 state 和 effect 這兩種 hooks 去請(qǐng)求數(shù)據(jù)。我們將使用眾所周知的 Hacker News API 來(lái)獲取一些熱門(mén)文章。你將定義屬于你自己的數(shù)據(jù)請(qǐng)求的 Hooks ,并且可以在你所有的應(yīng)用中復(fù)用,也可以發(fā)布到 npm 。
如果你不了解 React 的這些新特性,可以查看我的另一篇文章 introduction to React Hooks。如果你想直接查看文章的示例,可以直接 checkout 這個(gè) Github 倉(cāng)庫(kù)。
注意:在 React 未來(lái)的版本中,Hooks 將不會(huì)用了獲取數(shù)據(jù),取而代之的是一種叫做 Suspense 的東西。盡管如此,下面的方法依然是了解 state 和 effect 兩種 Hooks 的好方法。使用 React Hooks 進(jìn)行數(shù)據(jù)請(qǐng)求
如果你沒(méi)有過(guò)在 React 中進(jìn)行數(shù)據(jù)請(qǐng)求的經(jīng)驗(yàn),可以閱讀我的文章:How to fetch data in React。文章講解了如何使用 Class components 獲取數(shù)據(jù),如何使用可重用的 Render Props Components 和 Higher Order Components ,以及如何進(jìn)行錯(cuò)誤處理和 loading 狀態(tài)。在本文中,我想用 Function components 和 React Hooks 來(lái)重現(xiàn)這一切。
import React, { useState } from "react"; function App() { const [data, setData] = useState({ hits: [] }); return (
App 組件將展示一個(gè)列表,列表信息來(lái)自 Hacker News articles 。狀態(tài)和狀態(tài)更新函數(shù)將通過(guò)被稱(chēng)為 useState 的狀態(tài)鉤子來(lái)生成,它負(fù)責(zé)管理通過(guò)請(qǐng)求得到的 App 組件的本地狀態(tài)。初始狀態(tài)是一個(gè)空數(shù)組,目前沒(méi)有任何地方給它設(shè)置新的狀態(tài)。
我們將使用 axios 來(lái)獲取數(shù)據(jù),當(dāng)然也可以使用你熟悉的請(qǐng)求庫(kù),或者瀏覽器自帶的 fetch API。如果你還沒(méi)有安裝過(guò) axios ,可以通過(guò) npm install axios 進(jìn)行安裝。
import React, { useState, useEffect } from "react"; import axios from "axios"; function App() { const [data, setData] = useState({ hits: [] }); useEffect(async () => { const result = await axios( "http://hn.algolia.com/api/v1/search?query=redux", ); setData(result.data); }); return (
我們?cè)?useEffect 這個(gè) effect hook 中,通過(guò) axios 從 API 中獲取數(shù)據(jù),并使用 state hook 的更新函數(shù),將數(shù)據(jù)存入到本地 state 中。并且使用 async/await 來(lái)解析promise。
然而,當(dāng)你運(yùn)行上面的代碼的時(shí)候,你會(huì)陷入到該死的死循環(huán)中。effect hook 在組件 mount 和 update 的時(shí)候都會(huì)執(zhí)行。因?yàn)槲覀兠看潍@取數(shù)據(jù)后,都會(huì)更新 state,所以組件會(huì)更新,并再次運(yùn)行 effect,這會(huì)一次又一次的請(qǐng)求數(shù)據(jù)。很明顯我們需要避免這樣的bug產(chǎn)生,我們只想在組件 mount 的時(shí)候請(qǐng)求數(shù)據(jù)。你可以在 effect hook 提供的第二個(gè)參數(shù)中,傳入一個(gè)空數(shù)組,這樣做可以避免組件更新的時(shí)候執(zhí)行 effect hook ,但是組件在 mount 依然會(huì)執(zhí)行它。
import React, { useState, useEffect } from "react"; import axios from "axios"; function App() { const [data, setData] = useState({ hits: [] }); useEffect(async () => { const result = await axios( "http://hn.algolia.com/api/v1/search?query=redux", ); setData(result.data); }, []); return (
第二個(gè)參數(shù)是用來(lái)定義 hook 所以依賴(lài)的變量的。如果其中一個(gè)變量發(fā)生變化,hook 將自動(dòng)運(yùn)行。如果第二個(gè)參數(shù)是一個(gè)空數(shù)組,那么 hook 將不會(huì)在組件更新是運(yùn)行,因?yàn)樗鼪](méi)有監(jiān)控任何的變量。
還有一個(gè)需要特別注意的點(diǎn),在代碼中,我們使用了 async/await 來(lái)獲取第三方 API 提供的數(shù)據(jù)。根據(jù)文檔,每一個(gè) async 函數(shù)都將返回一個(gè)隱式的 promise:
"The async function declaration defines an asynchronous function, which returns an AsyncFunction object. An asynchronous function is a function which operates asynchronously via the event loop, using an implicit Promise to return its result. "“async 函數(shù)定義了一個(gè)異步函數(shù),它返回的是一個(gè)異步函數(shù)對(duì)象,異步函數(shù)是一個(gè)通過(guò)事件循環(huán)進(jìn)行操作的函數(shù),使用隱式的 Promise 返回最終的結(jié)果。”
然而,effect hook 應(yīng)該是什么也不返回的,或者返回一個(gè) clean up 函數(shù)的。這就是為什么你會(huì)在控制臺(tái)看到一個(gè)錯(cuò)誤信息。
index.js:1452 Warning: useEffect function must return a cleanup function or nothing. Promises and useEffect(async () => …) are not supported, but you can call an async function inside an effect.
這意味著我們不能直接在 useEffect 函數(shù)使用async。讓我們來(lái)實(shí)現(xiàn)一個(gè)解決方案,能夠在 effect hook 中使用 async 函數(shù)。
import React, { useState, useEffect } from "react"; import axios from "axios"; function App() { const [data, setData] = useState({ hits: [] }); useEffect(() => { const fetchData = async () => { const result = await axios( "http://hn.algolia.com/api/v1/search?query=redux", ); setData(result.data); }; fetchData(); }, []); return (
這就是一個(gè)使用 React Hooks 進(jìn)行數(shù)據(jù)請(qǐng)求的小案例。但是,如果你對(duì)錯(cuò)誤處理、loading 態(tài)、如何觸發(fā)表單數(shù)據(jù)獲取以及如何復(fù)用出具處理 hook 感興趣,那我們接著往下看。
如何手動(dòng)或者自動(dòng)觸發(fā)一個(gè) hook?現(xiàn)在我們已經(jīng)能夠在組件 mount 之后獲取到數(shù)據(jù),但是,如何使用輸入框動(dòng)態(tài)告訴 API 選擇一個(gè)感興趣的話題呢?可以看到之前的代碼,我們默認(rèn)將 "Redux" 作為查詢(xún)參數(shù)("http://hn.algolia.com/api/v1/..."),但是我們?cè)趺床樵?xún)關(guān)于 React 相關(guān)的話題呢?讓我們實(shí)現(xiàn)一個(gè) input 輸入框,可以獲得除了 “Redux” 之外的其他的話題?,F(xiàn)在,讓我們?yōu)檩斎肟蛞胍粋€(gè)新的 state。
import React, { Fragment, useState, useEffect } from "react"; import axios from "axios"; function App() { const [data, setData] = useState({ hits: [] }); const [query, setQuery] = useState("redux"); useEffect(() => { const fetchData = async () => { const result = await axios( "http://hn.algolia.com/api/v1/search?query=redux", ); setData(result.data); }; fetchData(); }, []); return (setQuery(event.target.value)} /> ); } export default App;{data.hits.map(item => (
- {item.title}
))}
現(xiàn)在,請(qǐng)求數(shù)據(jù)和查詢(xún)參數(shù)兩個(gè) state 相互獨(dú)立,但是我們需要像一個(gè)辦法希望他們耦合起來(lái),只獲取輸入框輸入的參數(shù)指定的話題文章。通過(guò)以下修改,組件應(yīng)該在 mount 之后按照查詢(xún)獲取相應(yīng)文章。
... function App() { const [data, setData] = useState({ hits: [] }); const [query, setQuery] = useState("redux"); useEffect(() => { const fetchData = async () => { const result = await axios( `http://hn.algolia.com/api/v1/search?query=${query}`, ); setData(result.data); }; fetchData(); }, []); return ( ... ); } export default App;
實(shí)際上,我們還缺少部分代碼。你會(huì)發(fā)現(xiàn)當(dāng)你在輸入框輸入內(nèi)容后,并沒(méi)有獲取到新的數(shù)據(jù)。這是因?yàn)?useEffect 的第二個(gè)參數(shù)只是一個(gè)空數(shù)組,此時(shí)的 effect 不依賴(lài)于任何的變量,所以這只會(huì)在 mount 只會(huì)觸發(fā)一次。但是,現(xiàn)在我們需要依賴(lài)查詢(xún)條件,一旦查詢(xún)發(fā)送改變,數(shù)據(jù)請(qǐng)求就應(yīng)該再次觸發(fā)。
... function App() { const [data, setData] = useState({ hits: [] }); const [query, setQuery] = useState("redux"); useEffect(() => { const fetchData = async () => { const result = await axios( `http://hn.algolia.com/api/v1/search?query=${query}`, ); setData(result.data); }; fetchData(); }, [query]); return ( ... ); } export default App;
好了,現(xiàn)在一旦你改變輸入框內(nèi)容,數(shù)據(jù)就會(huì)重新獲取。但是現(xiàn)在又要另外一個(gè)問(wèn)題:每次輸入一個(gè)新字符,就會(huì)觸發(fā) effect 進(jìn)行一次新的請(qǐng)求。那么我們提供一個(gè)按鈕來(lái)手動(dòng)觸發(fā)數(shù)據(jù)請(qǐng)求呢?
function App() { const [data, setData] = useState({ hits: [] }); const [query, setQuery] = useState("redux"); const [search, setSearch] = useState("redux"); useEffect(() => { const fetchData = async () => { const result = await axios( `http://hn.algolia.com/api/v1/search?query=${search}`, ); setData(result.data); }; fetchData(); }, [search]); return (setQuery(event.target.value)} /> ); }{data.hits.map(item => (
- {item.title}
))}
此外,search state 的初始狀態(tài)也是設(shè)置成了與 query state 相同的狀態(tài),因?yàn)榻M件在 mount 的時(shí)候會(huì)請(qǐng)求一次數(shù)據(jù),此時(shí)的結(jié)果也應(yīng)該是反應(yīng)的是輸入框中的搜索條件。然而, search state 和 query state 具有類(lèi)似的值,這看起來(lái)比較困惑。為什么不將真實(shí)的 URL 設(shè)置到 search state 中呢?
function App() { const [data, setData] = useState({ hits: [] }); const [query, setQuery] = useState("redux"); const [url, setUrl] = useState( "http://hn.algolia.com/api/v1/search?query=redux", ); useEffect(() => { const fetchData = async () => { const result = await axios(url); setData(result.data); }; fetchData(); }, [url]); return (setQuery(event.target.value)} /> ); }{data.hits.map(item => (
- {item.title}
))}
這就是通過(guò) effect hook 獲取數(shù)據(jù)的案例,你可以決定 effect 取決于哪個(gè) state。在這個(gè)案例中,如果 URL 的 state 發(fā)生改變,則再次運(yùn)行該 effect 通過(guò) API 重新獲取主題文章。
Loading 態(tài) 與 React Hooks讓我們?cè)跀?shù)據(jù)的加載過(guò)程中引入一個(gè) Loading 狀態(tài)。它只是另一個(gè)由 state hook 管理的狀態(tài)。Loading state 用于在 App 組件中呈現(xiàn) Loading 狀態(tài)。
import React, { Fragment, useState, useEffect } from "react"; import axios from "axios"; function App() { const [data, setData] = useState({ hits: [] }); const [query, setQuery] = useState("redux"); const [url, setUrl] = useState( "http://hn.algolia.com/api/v1/search?query=redux", ); const [isLoading, setIsLoading] = useState(false); useEffect(() => { const fetchData = async () => { setIsLoading(true); const result = await axios(url); setData(result.data); setIsLoading(false); }; fetchData(); }, [url]); return (setQuery(event.target.value)} /> {isLoading ? ( ); } export default App;Loading ...) : ({data.hits.map(item => (
)}- {item.title}
))}
現(xiàn)在當(dāng)組件處于 mount 狀態(tài)或者 URL state 被修改時(shí),調(diào)用 effect 獲取數(shù)據(jù),Loading 狀態(tài)就會(huì)變成 true。一旦請(qǐng)求完成,Loading 狀態(tài)就會(huì)再次被設(shè)置為 false。
錯(cuò)誤處理與 React Hooks通過(guò) React Hooks 進(jìn)行數(shù)據(jù)請(qǐng)求時(shí),如何進(jìn)行錯(cuò)誤處理呢? 錯(cuò)誤只是另一個(gè)使用 state hook 初始化的另一種狀態(tài)。一旦出現(xiàn)錯(cuò)誤狀態(tài),App 組件就可以反饋給用戶(hù)。當(dāng)使用 async/await 函數(shù)時(shí),通常使用 try/catch 來(lái)進(jìn)行錯(cuò)誤捕獲,你可以在 effect 中進(jìn)行下面操作:
... const [isError, setIsError] = useState(false); useEffect(() => { const fetchData = async () => { setIsError(false); setIsLoading(true); try { const result = await axios(url); setData(result.data); } catch (error) { setIsError(true); } setIsLoading(false); }; fetchData(); }, [url]); return (... {isError && Something went wrong ...} ...);
effect 每次運(yùn)行都會(huì)重置 error state 的狀態(tài),這很有用,因?yàn)槊看握?qǐng)求失敗后,用戶(hù)可能重新嘗試,這樣就能夠重置錯(cuò)誤。為了觀察代碼是否生效,你可以填寫(xiě)一個(gè)無(wú)用的 URL ,然后檢查錯(cuò)誤信息是否會(huì)出現(xiàn)。
使用表單進(jìn)行數(shù)據(jù)獲取什么才是獲取數(shù)據(jù)的正確形式呢?現(xiàn)在我們只有輸入框和按鈕進(jìn)行組合,一旦引入更多的 input 元素,你可能想要使用表單來(lái)進(jìn)行包裝。此外表單還能夠觸發(fā)鍵盤(pán)的 “Enter” 事件。
function App() { ... const doFetch = (evt) => { evt.preventDefault(); setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`); } return (自定義 hook 獲取數(shù)據(jù){isError && ); }Something went wrong ...} ...
我們可以定義一個(gè)自定義的 hook,提取出所有與數(shù)據(jù)請(qǐng)求相關(guān)的東西,除了輸入框的 query state,除此之外還有 Loading 狀態(tài)、錯(cuò)誤處理。還要確保返回組件中需要用到的變量。
const useHackerNewsApi = () => { const [data, setData] = useState({ hits: [] }); const [url, setUrl] = useState( "http://hn.algolia.com/api/v1/search?query=redux", ); const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); useEffect(() => { const fetchData = async () => { setIsError(false); setIsLoading(true); try { const result = await axios(url); setData(result.data); } catch (error) { setIsError(true); } setIsLoading(false); }; fetchData(); }, [url]); const doFetch = () => { setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`); }; return { data, isLoading, isError, doFetch }; }
現(xiàn)在,我們?cè)?App 組件中使用我們的新 hook 。
function App() { const [query, setQuery] = useState("redux"); const { data, isLoading, isError, doFetch } = useHackerNewsApi(); return (... ); }
接下來(lái),在外部傳遞 URL 給 DoFetch 方法。
const useHackerNewsApi = () => { ... useEffect( ... ); const doFetch = url => { setUrl(url); }; return { data, isLoading, isError, doFetch }; }; function App() { const [query, setQuery] = useState("redux"); const { data, isLoading, isError, doFetch } = useHackerNewsApi(); return (... ); }
初始的 state 也是通用的,可以通過(guò)參數(shù)簡(jiǎn)單的傳遞到自定義的 hook 中:
import React, { Fragment, useState, useEffect } from "react"; import axios from "axios"; const useDataApi = (initialUrl, initialData) => { const [data, setData] = useState(initialData); const [url, setUrl] = useState(initialUrl); const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); useEffect(() => { const fetchData = async () => { setIsError(false); setIsLoading(true); try { const result = await axios(url); setData(result.data); } catch (error) { setIsError(true); } setIsLoading(false); }; fetchData(); }, [url]); const doFetch = url => { setUrl(url); }; return { data, isLoading, isError, doFetch }; }; function App() { const [query, setQuery] = useState("redux"); const { data, isLoading, isError, doFetch } = useDataApi( "http://hn.algolia.com/api/v1/search?query=redux", { hits: [] }, ); return ({isError && ); } export default App;Something went wrong ...} {isLoading ? (Loading ...) : ({data.hits.map(item => (
)}- {item.title}
))}
這就是使用自定義 hook 獲取數(shù)據(jù)的方法,hook 本身對(duì)API一無(wú)所知,它從外部獲取參數(shù),只管理必要的 state ,如數(shù)據(jù)、 Loading 和錯(cuò)誤相關(guān)的 state ,并且執(zhí)行請(qǐng)求并將數(shù)據(jù)通過(guò) hook 返回給組件。
用于數(shù)據(jù)獲取的 Reducer Hook目前為止,我們已經(jīng)使用 state hooks 來(lái)管理了我們獲取到的數(shù)據(jù)數(shù)據(jù)、Loading 狀態(tài)、錯(cuò)誤狀態(tài)。然而,所有的狀態(tài)都有屬于自己的 state hook,但是他們又都連接在一起,關(guān)心的是同樣的事情。如你所見(jiàn),所有的它們都在數(shù)據(jù)獲取函數(shù)中被使用。它們一個(gè)接一個(gè)的被調(diào)用(比如:setIsError、setIsLoading),這才是將它們連接在一起的正確用法。讓我們用一個(gè) Reducer Hook 將這三者連接在一起。
Reducer Hook 返回一個(gè) state 對(duì)象和一個(gè)函數(shù)(用來(lái)改變 state 對(duì)象)。這個(gè)函數(shù)被稱(chēng)為分發(fā)函數(shù)(dispatch function),它分發(fā)一個(gè) action,action 具有 type 和 payload 兩個(gè)屬性。所有的這些信息都在 reducer 函數(shù)中被接收,根據(jù)之前的狀態(tài)提取一個(gè)新的狀態(tài)。讓我們看看在代碼中是如何工作的:
import React, { Fragment, useState, useEffect, useReducer, } from "react"; import axios from "axios"; const dataFetchReducer = (state, action) => { ... }; const useDataApi = (initialUrl, initialData) => { const [url, setUrl] = useState(initialUrl); const [state, dispatch] = useReducer(dataFetchReducer, { isLoading: false, isError: false, data: initialData, }); ... };
Reducer Hook 以 reducer 函數(shù)和一個(gè)初始狀態(tài)對(duì)象作為參數(shù)。在我們的案例中,加載的數(shù)據(jù)、Loading 狀態(tài)、錯(cuò)誤狀態(tài)都是作為初始狀態(tài)參數(shù),且不會(huì)發(fā)生改變,但是他們被聚合到一個(gè)狀態(tài)對(duì)象中,由 reducer hook 管理,而不是單個(gè) state hooks。
const dataFetchReducer = (state, action) => { ... }; const useDataApi = (initialUrl, initialData) => { const [url, setUrl] = useState(initialUrl); const [state, dispatch] = useReducer(dataFetchReducer, { isLoading: false, isError: false, data: initialData, }); useEffect(() => { const fetchData = async () => { dispatch({ type: "FETCH_INIT" }); try { const result = await axios(url); dispatch({ type: "FETCH_SUCCESS", payload: result.data }); } catch (error) { dispatch({ type: "FETCH_FAILURE" }); } }; fetchData(); }, [url]); ... };
現(xiàn)在,在獲取數(shù)據(jù)時(shí),可以使用 dispatch 函數(shù)向 reducer 函數(shù)發(fā)送信息。使用 dispatch 函數(shù)發(fā)送的對(duì)象具有一個(gè)必填的 type 屬性和一個(gè)可選的 payload 屬性。type 屬性告訴 reducer 函數(shù)需要轉(zhuǎn)換的 state 是哪個(gè),還可以從 payload 中提取新的 state。在這里只有三個(gè)狀態(tài)轉(zhuǎn)換:初始化數(shù)據(jù)過(guò)程,通知數(shù)據(jù)請(qǐng)求成功的結(jié)果,以及通知數(shù)據(jù)請(qǐng)求失敗的結(jié)果。
在自定義 hook 的末尾,state 像以前一樣返回,但是因?yàn)槲覀兯械?state 都在一個(gè)對(duì)象中,而不再是獨(dú)立的 state ,所以 state 對(duì)象進(jìn)行解構(gòu)返回。這樣,調(diào)用 useDataApi 自定義 hook 的人仍然可以 data 、isLoading和isError:
const useDataApi = (initialUrl, initialData) => { const [url, setUrl] = useState(initialUrl); const [state, dispatch] = useReducer(dataFetchReducer, { isLoading: false, isError: false, data: initialData, }); ... const doFetch = url => { setUrl(url); }; return { ...state, doFetch }; };
最后我們還缺少 reducer 函數(shù)的實(shí)現(xiàn)。它需要處理三個(gè)不同的狀態(tài)轉(zhuǎn)換,分被稱(chēng)為 FEATCH_INIT、FEATCH_SUCCESS、FEATCH_FAILURE。每個(gè)狀態(tài)轉(zhuǎn)換都需要返回一個(gè)新的狀態(tài)。讓我們看看使用 switch case 如何實(shí)現(xiàn)這個(gè)邏輯:
const dataFetchReducer = (state, action) => { switch (action.type) { case "FETCH_INIT": return { ...state }; case "FETCH_SUCCESS": return { ...state }; case "FETCH_FAILURE": return { ...state }; default: throw new Error(); } };
reducer 函數(shù)可以通過(guò)其參數(shù)訪問(wèn)當(dāng)前狀態(tài)和 dispatch 傳入的 action。到目前為止,在 switch case 語(yǔ)句中,每個(gè)狀態(tài)轉(zhuǎn)換只返回前一個(gè)狀態(tài),析構(gòu)語(yǔ)句用于保持 state 對(duì)象不可變(即狀態(tài)永遠(yuǎn)不會(huì)被直接更改)?,F(xiàn)在讓我們重寫(xiě)一些當(dāng)前 state 返回的屬性,以便在每次轉(zhuǎn)換時(shí)更改 一些 state:
const dataFetchReducer = (state, action) => { switch (action.type) { case "FETCH_INIT": return { ...state, isLoading: true, isError: false }; case "FETCH_SUCCESS": return { ...state, isLoading: false, isError: false, data: action.payload, }; case "FETCH_FAILURE": return { ...state, isLoading: false, isError: true, }; default: throw new Error(); } };
現(xiàn)在,每個(gè)狀態(tài)轉(zhuǎn)換(action.type決定)都返回一個(gè)基于先前 state 和可選 payload 的新?tīng)顟B(tài)。例如,在請(qǐng)求成功的情況下,payload 用于設(shè)置新 state 對(duì)象的 data 屬性。
總之,reducer hook 確保使用自己的邏輯封裝狀態(tài)管理的這一部分。通過(guò)提供 action type 和可選 payload ,總是會(huì)得到可預(yù)測(cè)的狀態(tài)更改。此外,永遠(yuǎn)不會(huì)遇到無(wú)效狀態(tài)。例如,以前可能會(huì)意外地將 isLoading 和 isError 設(shè)置為true。在這種情況下,UI中應(yīng)該顯示什么? 現(xiàn)在,由 reducer 函數(shù)定義的每個(gè) state 轉(zhuǎn)換都指向一個(gè)有效的 state 對(duì)象。
在 Effect Hook 中中斷數(shù)據(jù)請(qǐng)求在React中,即使組件已經(jīng)卸載,組件 state 仍然會(huì)被被賦值,這是一個(gè)常見(jiàn)的問(wèn)題。我在之前的文章中寫(xiě)過(guò)這個(gè)問(wèn)題,它描述了如何防止在各種場(chǎng)景中為未掛載組件設(shè)置狀態(tài)。讓我們看看在自定義 hook 中,請(qǐng)求數(shù)據(jù)時(shí)如何防止設(shè)置狀態(tài):
const useDataApi = (initialUrl, initialData) => { const [url, setUrl] = useState(initialUrl); const [state, dispatch] = useReducer(dataFetchReducer, { isLoading: false, isError: false, data: initialData, }); useEffect(() => { let didCancel = false; const fetchData = async () => { dispatch({ type: "FETCH_INIT" }); try { const result = await axios(url); if (!didCancel) { dispatch({ type: "FETCH_SUCCESS", payload: result.data }); } } catch (error) { if (!didCancel) { dispatch({ type: "FETCH_FAILURE" }); } } }; fetchData(); return () => { didCancel = true; }; }, [url]); const doFetch = url => { setUrl(url); }; return { ...state, doFetch }; };
每個(gè)Effect Hook都帶有一個(gè)clean up函數(shù),它在組件卸載時(shí)運(yùn)行。clean up 函數(shù)是 hook 返回的一個(gè)函數(shù)。在該案例中,我們使用 didCancel 變量來(lái)讓 fetchData 知道組件的狀態(tài)(掛載/卸載)。如果組件確實(shí)被卸載了,則應(yīng)該將標(biāo)志設(shè)置為 true,從而防止在最終異步解析數(shù)據(jù)獲取之后設(shè)置組件狀態(tài)。
注意:實(shí)際上并沒(méi)有中止數(shù)據(jù)獲?。ú贿^(guò)可以通過(guò)Axios取消來(lái)實(shí)現(xiàn)),但是不再為卸載的組件執(zhí)行狀態(tài)轉(zhuǎn)換。由于 Axios 取消在我看來(lái)并不是最好的API,所以這個(gè)防止設(shè)置狀態(tài)的布爾標(biāo)志也可以完成這項(xiàng)工作。
原文鏈接
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/102986.html
摘要:而每次捕獲出的錯(cuò)誤可能需要打印出來(lái)以檢測(cè)。同時(shí)有些同學(xué)不習(xí)慣使用來(lái)捕獲錯(cuò)誤,這就可能造成不可預(yù)計(jì)的問(wèn)題。是否立即請(qǐng)求并接受初始化返回值業(yè)務(wù)我們并不希望初始化的是否立即發(fā)送請(qǐng)求。前言 自 React Hooks 16.8.0 后帶來(lái)了 React hooks 這一特性。這一特性在沒(méi)有破壞性的更新下為我們帶來(lái)了更加舒爽的開(kāi)發(fā)方式。過(guò)去我們常常因providers,consumers,高階組件,...
摘要:可以看到,這樣不僅沒(méi)有占用組件自己的,也不需要手寫(xiě)回調(diào)函數(shù)進(jìn)行處理,這些處理都?jí)嚎s成了一行。效果通過(guò)拿到周期才執(zhí)行的回調(diào)函數(shù)。實(shí)現(xiàn)等價(jià)于的回調(diào)僅執(zhí)行一次時(shí),因此直接把回調(diào)函數(shù)拋出來(lái)即可。 1 引言 上周的 精讀《React Hooks》 已經(jīng)實(shí)現(xiàn)了對(duì) React Hooks 的基本認(rèn)知,也許你也看了 React Hooks 基本實(shí)現(xiàn)剖析(就是數(shù)組),但理解實(shí)現(xiàn)原理就可以用好了嗎?學(xué)的是...
摘要:本次需求其實(shí)就兩個(gè)邏輯輸入篩選項(xiàng)。當(dāng)發(fā)生改變時(shí),重新渲染頁(yè)面首次進(jìn)入頁(yè)面時(shí),無(wú)任何篩選項(xiàng)。關(guān)于的一些,官方也有很棒的文檔寫(xiě)在后面本文通過(guò)工作中的一個(gè)小需求,完成了一次的實(shí)踐,不過(guò)上述代碼依然有很多需要優(yōu)化的地方。 寫(xiě)在前面 showImg(https://segmentfault.com/img/bVbpBgw?w=1000&h=563); 本文首發(fā)于公眾號(hào):符合預(yù)期的CoyPan R...
摘要:在這種情況下,如果狀態(tài)發(fā)生變化,將再次運(yùn)行以從獲取數(shù)據(jù)。你可以在內(nèi)做到在表單中獲取數(shù)據(jù)到目前為止,我們只有和按鈕的組合?,F(xiàn)在,在獲取數(shù)據(jù)時(shí),可以使用向函數(shù)發(fā)送信息。例如,在成功請(qǐng)求的情況下,用于設(shè)置新?tīng)顟B(tài)對(duì)象的數(shù)據(jù)。 原文鏈接: https://www.robinwieruch.de/r... 在本教程中,我想通過(guò)state和effect hook來(lái)像你展示如何用React Hook...
摘要:比如在條件判斷中使用,在循環(huán),嵌套函數(shù)中使用,都會(huì)造成執(zhí)行順序不一致的問(wèn)題。而比如定時(shí)器,事件監(jiān)聽(tīng)。第一個(gè)參數(shù)的返回值,會(huì)在組件卸載時(shí)執(zhí)行,相當(dāng)于,可以清理定時(shí)器,移除事件監(jiān)聽(tīng),取消一些訂閱。 什么是 Hooks? 不通過(guò)編寫(xiě)類(lèi)組件的情況下,可以在組件內(nèi)部使用狀態(tài)(state) 和其他 React 特性(生命周期,context)的技術(shù) Hooks 為什么會(huì)出現(xiàn) 在之前的 React ...
閱讀 2767·2021-11-24 10:23
閱讀 1164·2021-11-17 09:33
閱讀 2512·2021-09-28 09:41
閱讀 1427·2021-09-22 15:55
閱讀 3649·2019-08-29 16:32
閱讀 1916·2019-08-29 16:25
閱讀 1065·2019-08-29 11:06
閱讀 3431·2019-08-29 10:55