原文连接前端
这是我在《the Road to React》的做者Robin Wieruchu的博客中看到的一篇关于如何使用React Hooks获取数据的一篇文章,之因此想翻译,是以为这篇文章能够按部就班地为读者介绍,在不一样的场景下如何使用正确的React Hooks,若是您对React Hooks不是很了解,相信读完这篇文章会对其有新的认识,好吧,闲话很少说,接下来,翻译正文开始。react
在本教程中,我将向您展现如何使用React Hooks中的state以及effect来获取数据。咱们将使用广为人知的Hacker News API从科技界获取热门文章。你还将实现用于数据获取的自定义钩子函数,该钩子函数可在程序中的任何位置复用,或者做为独立的包发布在npm上。ios
若是您对这个新的React功能一无所知,请查看此React Hooks简介。若是您想查看项目代码,想知道如何使用React Hooks获取数据,能够查看这个代码仓库。git
若是您只想使用React Hooks进行数据获取操做,能够执行npm install use-data-api
,请按照文档进行操做。若是使用它,别忘了给个Star哦。github
若是您不熟悉React中的数据获取,请查看个人 extensive data fetching in React article。它带您逐步了解如何使用React类组件获取数据,如何经过Render Prop 组件和高阶组件使其变为可复用组件,以及如何进行error处理以及loading状态处理。在本文中,我想经过函数组件中的React Hooks向您展现这些内容。npm
import React, { useState } from 'react';
function App() {
const [data, setData] = useState({ hits: [] });
return (
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
);
}
export default App;
复制代码
App组件显示项目列表(hits = Hacker News文章)。 经过useState,能够查看state或者更新state,从而管理App组件所获取的数据,初始状态中{ hits: [] }
表明一个空列表,此时还未进行数据获取。编程
咱们将使用axios
来获取数据,固然使用其余第三方库也是能够的。若是还没有安装axios
,则能够在命令行中使用来安装npm install axios
。而后实现数据获取的effect hook
:redux
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function App() {
const [data, setData] = useState({ hits: [] });
useEffect(async () => {
const result = await axios(
'https://hn.algolia.com/api/v1/search?query=redux',
);
setData(result.data);
});
return (
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
);
}
export default App;
复制代码
反作用钩子函数useEffect用于经过axios提取数据,并使用useState中的update函数来更新状态,从而更新视图,promise经过async/await来处理。axios
可是,当您运行应用程序时,应该陷入一个讨厌的循环。useEffect在组件挂载阶段运行,同时也在组件update阶段运行。由于咱们在每次获取数据后都要更新state,此时组件会update,那useEffect又会执行,从而又进行state更新(ps:首次加载-> useEffect -> 更新state -> update阶段再次执行useEffect -> 再次更新state......无限循环)。它一次又一次地获取数据,要避免这种错误。咱们只想在组件挂载时获取数据。所以,您能够为效果挂钩提供一个空数组[]
做为第二个参数,以免在组件更新时调用,而仅在组件挂载阶段调用它。api
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function App() {
const [data, setData] = useState({ hits: [] });
useEffect(async () => {
const result = await axios(
'https://hn.algolia.com/api/v1/search?query=redux',
);
setData(result.data);
}, []); // 这样就只会在挂载阶段执行反作用了
return (
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
);
}
export default App;
复制代码
useEffect第二个参数可用于定义挂钩所依赖的全部变量(在此数组中分配)。若是变量之一更改,则挂钩再次运行。若是带有变量的数组为空,则在更新组件时此钩子函数根本不会运行,由于它没必要监视任何变量。
最后一招。在代码中,咱们使用async / await从第三方API获取数据。根据文档,每一个带有async注释的函数都会返回一个隐含的promise,可是,useEffect应不返回任何内容或返回一个清除函数。这就是为何您可能在开发人员控制台日志中看到如下警告:useEffect函数必须返回清除函数,不然不返回任何内容。不支持Promises和useEffect(async()=> ...),可是您能够在useEffect内部调用异步函数。这就是为何useEffect不容许直接在函数中使用异步的缘由。让咱们经过使用useEffect内的异步函数来实现此变通方法。
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(
'https://hn.algolia.com/api/v1/search?query=redux',
);
setData(result.data);
};
fetchData(); // 经过调用异步函数,来避免直接异步调用的报错
}, []);
return (
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
);
}
export default App;
复制代码
简而言之,这就是使用React Hooks获取数据。可是若是您对error处理,loading指示器,如何触发从表单中获取数据以及如何实现可复用的数据获取钩子函数感兴趣,请继续阅读。
太好了,一旦组件挂载完毕咱们就获取数据。可是,如何使用输入字段来告诉API咱们感兴趣的主题呢?“ Redux”被用做默认查询。可是关于“React”的话题呢?让咱们实现一个input元素,使咱们可以获取“ Redux”文章之外的其余文章。所以,为输入元素引入新的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(
'https://hn.algolia.com/api/v1/search?query=redux',
);
setData(result.data);
};
fetchData();
}, []);
return (
<Fragment>
<input
type="text"
value={query}
onChange={event => setQuery(event.target.value)}
/>
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
</Fragment>
);
}
export default App;
复制代码
目前,这两种状态彼此独立,可是如今您但愿它们仅在输入时获取指定文章从而关联起来,进行如下更改后,该组件会在挂载后按输入的内容查询全部文章。
...
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}`, // 添加query
);
setData(result.data);
};
fetchData();
}, []);
return (
...
);
}
export default App;
复制代码
还缺乏一件事:当您尝试在输入框中输入内容时,从useEffect触发以后,就不会再获取其余数据。那是由于您提供了空数组[]
做为效果的第二个参数。该反作用不依赖任何变量,所以仅在挂载组件时触发。可是,如今效果应取决于query的内容。query内容更改后,数据请求应再次触发。
...
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]); // 这样useEffect就会在query发生改变时调用
return (
...
);
}
export default App;
复制代码
更改输入内容后,数据应该会从新获取。但这带来了另外一个问题:在输入框键入每一个字符时,都会触发useEffect并执行另外一个数据获取请求。如何提供一个触发请求的按钮,从而手动触发钩子?
function App() {
const [data, setData] = useState({ hits: [] });
const [query, setQuery] = useState('redux');
const [search, setSearch] = useState('');
useEffect(() => {
const fetchData = async () => {
const result = await axios(
`http://hn.algolia.com/api/v1/search?query=${query}`,
);
setData(result.data);
};
fetchData();
}, [query]);
return (
<Fragment>
<input
type="text"
value={query}
onChange={event => setQuery(event.target.value)}
/>
<button type="button" onClick={() => setSearch(query)}> // 经过按钮点击事件来触发查询操做
Search
</button>
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
</Fragment>
);
复制代码
如今,使useEffect取决于search状态,而不是随输入内容而变化的波动query状态。用户单击按钮后,便会设置新的search状态,并应手动触发反作用钩子函数。
...
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]); // 此时反作用钩子函数依赖search状态,点击时会触发反作用钩子函数。
return (
...
);
}
export default App;
复制代码
search的初始状态也被设置为与query状态相同的数据,该组件在挂载阶段获取数据,所以query反映输入字段中的值。可是,具备相似的query和search状态有点使人困惑。为何不将实际URL设置为状态而是search状态?
function App() {
const [data, setData] = useState({ hits: [] });
const [query, setQuery] = useState('redux');
const [url, setUrl] = useState(
'https://hn.algolia.com/api/v1/search?query=redux',
);
useEffect(() => {
const fetchData = async () => {
const result = await axios(url);
setData(result.data);
};
fetchData();
}, [url]);
return (
<Fragment>
<input
type="text"
value={query}
onChange={event => setQuery(event.target.value)}
/>
<button
type="button"
onClick={() =>
setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
}
>
Search
</button>
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
</Fragment>
);
}
复制代码
若是是使用useEffect进行隐式编程数据获取的话,您能够决定反作用取决于哪一个状态,在单击或其余反作用上设置此状态后,该effect hook将再次运行。在这种状况下,若是URL状态发生更改,则effect hook将再次运行请求API以获取数据。
让咱们为数据获取引入一个loading指示器。这只是由state hook管理的另外一个状态。loading标识用于在App组件中呈现loading指示器。
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(
'https://hn.algolia.com/api/v1/search?query=redux',
);
const [isLoading, setIsLoading] = useState(false); // 初始设置loading状态为false
useEffect(() => {
const fetchData = async () => {
setIsLoading(true); // 初始设置loading状态为true
const result = await axios(url);
setData(result.data);
setIsLoading(false); // 数据返回后设置loading状态为false
};
fetchData();
}, [url]);
return (
<Fragment>
<input
type="text"
value={query}
onChange={event => setQuery(event.target.value)}
/>
<button
type="button"
onClick={() =>
setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
}
>
Search
</button>
// 经过判断loading状态,来决定是否展现loading样式
{isLoading ? (
<div>Loading ...</div>
) : (
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
)}
</Fragment>
);
}
export default App;
复制代码
一旦调用了反作用钩子函数以进行数据获取(在组件挂载或URL状态更改时发生),则loading状态将设置为true。请求返回结果后,loading状态将再次设置为false。
使用React Hooks获取数据的错误处理怎么样?该错误只是使用state hook初始化的另外一个状态。一旦出现错误状态,App组件便可为用户提供反馈。使用async/await时,一般使用try / catch块进行错误处理。您能够在反作用钩子函数范围内作到这一点:
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(
'https://hn.algolia.com/api/v1/search?query=redux',
);
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
useEffect(() => {
const fetchData = async () => {
setIsError(false); // 请求开始设置error状态为false
setIsLoading(true);
try {
const result = await axios(url);
setData(result.data);
} catch (error) {
setIsError(true); // 若是catch到错误,即设置error状态为true
}
setIsLoading(false); // 最终loading状态设置为false,防止阻塞UI
};
fetchData();
}, [url]);
return (
<Fragment>
<input
type="text"
value={query}
onChange={event => setQuery(event.target.value)}
/>
<button
type="button"
onClick={() =>
setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
}
>
Search
</button>
{isError && <div>Something went wrong ...</div>} // error状态下展现相应UI
{isLoading ? (
<div>Loading ...</div>
) : (
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
)}
</Fragment>
);
}
export default App;
复制代码
每次钩子函数再次运行时,都会重置error状态。这颇有用,由于在失败的请求以后,用户可能想要再次尝试,因此应该重置error状态。为了本身执行错误,您能够将URL更改成无效的内容。而后检查是否显示错误消息。
如何用适当的形式来获取数据呢?到目前为止,咱们只有输入框和按钮的组合。引入更多input框后,您可能须要用form表单包装它们。此外,还能够经过form使用键盘上的“Enter”来触发按钮。
function App() {
...
return (
<Fragment>
<form
onSubmit={() =>
setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
}
>
<input
type="text"
value={query}
onChange={event => setQuery(event.target.value)}
/>
<button type="submit">Search</button>
</form>
{isError && <div>Something went wrong ...</div>}
...
</Fragment>
);
}
复制代码
可是如今,单击“提交”按钮时浏览器会从新加载,由于这是浏览器提交表单时的默认行为。为了防止默认行为,咱们能够在React事件上调用一个函数。这也是您在React类组件中执行此操做的方式。
function App() {
...
return (
<Fragment>
<form onSubmit={event => {
setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`);
event.preventDefault(); // 阻止提交表单后的刷新行为
}}>
<input
type="text"
value={query}
onChange={event => setQuery(event.target.value)}
/>
<button type="submit">Search</button>
</form>
{isError && <div>Something went wrong ...</div>}
...
</Fragment>
);
}
复制代码
如今,单击“提交”按钮后,浏览器再也不须要刷新。它像之前同样工做,可是此次使用的是表单而不是朴素的输入框和按钮组合。您也能够按键盘上的“ Enter”键。
为了抽出获取数据的自定义钩子函数,请将属于数据获取的全部内容(属于输入字段的search状态(还包括loading指示器和error处理))移至其自身的功能。另外,请确保您从App组件中使用的函数返回全部必需的数据。
const useHackerNewsApi = () => {
const [data, setData] = useState({ hits: [] });
const [url, setUrl] = useState(
'https://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]);
return [{ data, isLoading, isError }, setUrl]; // 最终返回全部必需的数据
}
复制代码
如今,新的钩子函数能够再次在App组件中使用:
function App() {
const [query, setQuery] = useState('redux');
// 封装了函数,经过数组解构直接返回数据获取函数、最终的数据、loading、error这些状态
const [{ data, isLoading, isError }, doFetch] = useHackerNewsApi();
return (
<Fragment>
<form onSubmit={event => {
doFetch(`http://hn.algolia.com/api/v1/search?query=${query}`);
event.preventDefault();
}}>
<input
type="text"
value={query}
onChange={event => setQuery(event.target.value)}
/>
<button type="submit">Search</button>
</form>
...
</Fragment>
);
}
复制代码
初始状态也能够设为通用。将其简单地传递到新的自定义钩子函数中:
import React, { Fragment, useState, useEffect } from 'react';
import axios from 'axios';
// 经过参数传入初始化url和初始化数据,使得数据获取的自定义钩子函数更纯
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]);
return [{ data, isLoading, isError }, setUrl];
};
function App() {
const [query, setQuery] = useState('redux');
const [{ data, isLoading, isError }, doFetch] = useDataApi(
'https://hn.algolia.com/api/v1/search?query=redux',
{ hits: [] },
);
return (
<Fragment>
<form
onSubmit={event => {
doFetch(
`http://hn.algolia.com/api/v1/search?query=${query}`,
);
event.preventDefault();
}}
>
<input
type="text"
value={query}
onChange={event => setQuery(event.target.value)}
/>
<button type="submit">Search</button>
</form>
{isError && <div>Something went wrong ...</div>}
{isLoading ? (
<div>Loading ...</div>
) : (
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
)}
</Fragment>
);
}
export default App;
复制代码
使用自定义钩子函数获取数据即将完成。其实钩子函数自己对API一无所知。它从外部接收全部参数,而且仅管理必要的状态,例如数据,loading和error状态。它执行请求并经过数据获取的自定义钩子函数,将数据返回给组件。
到目前为止,咱们已经使用各类状态钩子函数来管理数据的获取状态,loading和error状态。可是,全部的这些状态,管理本身的state hook,它们属于同一类,由于它们关心同一件事情,如您所见,它们都在数据获取函数中使用。一个好的指示器能够将一个又一个的state集结起来(例如:数据、loading、error状态),让咱们将它们所有三个与一个Reducer Hook结合使用。
Reducer Hook向咱们返回一个state对象和一个更改state对象的函数。该函数--称为disapatch,它采用action(拥有一个type和可选的payload)。全部这些信息都在实际的reducer功能中使用,以从先前state,经过操做的type和可选payload和中提取新state。让咱们看看这在代码中是如何工做的:
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);
// useReducer传入一个type和一个payload对象,经过数组结构得到state和dispatch
const [state, dispatch] = useReducer(dataFetchReducer, {
isLoading: false,
isError: false,
data: initialData,
});
...
};
复制代码
Reducer hook将一个reducer函数以及一个初始化的state对象做为参数。在咱们的例子中,数据,loading状态和error状态的初始状态的参数并无改变,可是它们已经聚合到一个由reducer钩子函数(useReducer)而不是单个useState管理的状态对象中。
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]);
...
};
复制代码
如今,在获取数据时,可使用dispatch函数将信息发送给reducer 函数。使用dispatch函数发送的action对象具备必填type属性和可选payload属性。该类型告诉reducer函数须要应用哪一个状态转换,而且reducer能够额外使用payload属性来提取新state对象。毕竟,咱们只有三个状态转换:初始化获取过程,通知成功的数据获取结果,以及通知错误的数据获取结果。
在自定义钩子函数的末尾,state将像之前同样返回,可是由于咱们有一个state对象,而再也不是独立state。这样一来,谁调用了一个useDataApi自定义的钩子函数仍然获得访问data,isLoading以及isError:
const useDataApi = (initialUrl, initialData) => {
const [url, setUrl] = useState(initialUrl);
const [state, dispatch] = useReducer(dataFetchReducer, {
isLoading: false,
isError: false,
data: initialData,
});
...
return [state, setUrl]; // 最终返回的state包含data、isError、isLoading
};
复制代码
最后一点,也是至关重要的一点,咱们还缺乏reducer函数的实现。它须要根据action对象中三种不一样的type(FETCH_INIT,FETCH_SUCCESS和FETCH_FAILURE),来进行三种不一样的状态转换,每一次转换都须要返回一个新的state对象,让咱们看看如何用switch case语句实现这一点:
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函数能够经过其参数访问state和action。到目前为止,在switch case语句中,每一个状态转换仅返回先前的状态。对象解构可让state对象保持不变-意味着state永远不会直接发生变化-来执行最佳实践。如今,让咱们重写一些当前state返回的属性,以在每次状态转换时更改state:
const dataFetchReducer = (state, action) => {
switch (action.type) {
case 'FETCH_INIT':
return {
...state, // 解构让咱们能够保持原先state不变
isLoading: true, // 只有当咱们手动重写state中的特定属性时,才会发生改变,这样很安全
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();
}
};
复制代码
如今,由action对象中的type属性决定的每一个状态转换都会根据先前state和可选payload返回一个新的状态。例如,在成功请求的状况下,payload用于设置新state对象的数据。
总之,Reducer Hook确保状态管理的这一部分使用其本身的逻辑进行封装。经过提供action对象中的type和可选的payload,您将始终返回一个新的数据实现状态变动。此外,您将永远不会陷入无效状态。例如,之前可能会意外地将isLoadingand isError状态同时设置为true。在这种状况下,UI中应显示什么?如今,由reducer函数定义的每一个状态转换声明都衍生出一个有效的state对象。
在React中,一个常见的问题是即便已卸载组件,会被设置state(例如,因为使用React Router导航)。在此以前,我已经写过有关此问题的文章,它描述了如何防止已卸载组件设置state。让咱们看看如何防止在自定义钩子函数中,在已卸载阶段仍然设置state:
const useDataApi = (initialUrl, initialData) => {
const [url, setUrl] = useState(initialUrl);
const [state, dispatch] = useReducer(dataFetchReducer, {
isLoading: false,
isError: false,
data: initialData,
});
useEffect(() => {
let didCancel = false; // useEffect执行时设置初始化didCancel为false
const fetchData = async () => {
dispatch({ type: 'FETCH_INIT' });
try {
const result = await axios(url);
if (!didCancel) { // 只有在didCancel为false的状况下,才会进行状态变动
dispatch({ type: 'FETCH_SUCCESS', payload: result.data });
}
} catch (error) {
if (!didCancel) { // 同上
dispatch({ type: 'FETCH_FAILURE' });
}
}
};
fetchData();
return () => {
// 重点:在useEffect中,若是最终返回一个清除函数,即该函数会在组件卸载阶段执行, 防止组件卸载后仍然设置状态
didCancel = true;
};
}, [url]);
return [state, setUrl];
};
复制代码
每一个Effect Hook都带有清理功能,该功能在卸载组件时运行。清理函数是从钩子函数中返回的一个函数。在咱们的例子中,咱们使用一个boolean标识didCancel来使咱们的数据获取逻辑知道组件的state(挂载/卸载)。若是确实卸载了组件,则应该设置该标识true,以防止在最终异步地解析了数据获取以后设置组件state。
注意:实际上,数据获取不会停止-这能够经过Axios Cancellation来实现-但已卸载的组件再也不执行状态转换。因为Axios Cancellation在我眼中并非最好的API,所以该防止设置状态的boolean标识也能够完成这项工做。
您已经了解了state和effect的React钩子函数如何在React中用于数据获取。若是您对使用render props和higher-order components(高阶组件)在类组件(和函数组件)中的数据获取感到好奇,请从头开始阅读个人其余文章。在此,我但愿本文对您了解React Hooks以及如何在实际场景中使用它们颇有帮助。
到这里也算翻译完成了,但愿阅读本文后,你能够对React Hooks有一个新的认识,若是你有以为很是优秀的英文前端文章,但愿能够翻译成中文的话,能够在留言评论,我会帮忙翻译一些你们都比较感兴趣的文章。