斜体样式本文大致的翻译了这篇文章
本文将向你展示如何通过 react 的 state 和 effect hook 获取数据。本文将使用 Hacker News API 获取科技类的热门文章,带你实现自定义的 react hook 来获取数据。
react hook 之获取数据
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 为函数组件提供状态和状态更新功能,它负责为 App 组件管理数据的局部状态。初始状态是 {hits: []},hits 为空数组。此时没有任何地方修改这个状态。
接下来使用 axios 这个库来获取数据,如果你还未安装,可以通过 npm install axios -S
安装一下。
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;
在 effect hook useEffect 里通过 axios 请求数据,并通过 state hook 的更新函数 setData 来将请求到的数据设置到组件的局部状态 data 里。
但是这时运行起来的代码应该会陷入死循环,因为组件挂载后会运行 useEffect 里的 axios 代码获取数据,然后更新状态 data,继而引发组件更新,而组件更新后 useEffect 又会再次运行,再次获取数据,再次触发组件更新。如此反复,进入死循环。我们只希望在组件挂载时请求一次数据,后续不再触发。我们可以给 useEffect 的第二个参数传入一个空数组,如下:
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 依赖的变量,只要依赖的变量发生变化,这个 hook 就会再次运行。而如果这个数组是一个空数组,那么这个 hook 不会再组件更新时运行,因为它不依赖任何变量。
到这里还有最后一个问题,我们用了 async / await 来获取数据,按照标准 async 函数会返回一个隐式的 promise。然而,effect hook 不能返回任何任何东西,也不能返回函数。所以你会看到一个提示 Warning: An effect function must not return anything besides a function, which is used for clean-up.
所以直接在 useEffect 里使用 async 函数是不允许的,改下代码,如下:
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 hook 里获取数据的简单示例。如果你对错误处理、loading 状态以及如何在 form 表单中获取数据和封装一个可复用的数据获取 hook 感兴趣的话,可以继续阅读下去。
如何自动或手动触发获取数据
现在通过这个组件已经能获取数据了,可是如何通过数据关键词来搜索想要的文章呢。我们添加一个输入框,用于输入搜索关键词,为此需要引入新的状态:
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