使用 react hooks 优雅的获取数据
写在最前面
- 适用于 react,es6使用者,react hooks 初学者。
- 本文主要写关于怎么使用
state
和effect
hooks 来优雅的获取列表数据。- 怎么定制一个获取数据的 hook?
- 当然你需要先了解一下 react hooks 的新特性
使用 hook 获取数据
- 这里有一篇讲解在 react 中怎么去获取数据 react 怎么优雅的获取数据
- 下面看看怎么使用 hook 来获取
1、useState的使用
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;
复制代码
state
, hooks 主动去回调userState
方法,把data
存储在state
中。
2、Axios 的使用(useEffect的使用)
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',
);
// 使用 useEffect 的时候,我们主动设置 `state` ,存储 `setData`
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;
复制代码
当你运行上面的程序的时候会发现有 bug ,这是为什么啦?因为当我们在获取数据后存储数据到 state 中的时候,我们的组件会随之更新,然后
effect
会再次运行一次。然后我们会又获取一次data
。
-
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..
-
怎么避免上面的问题啦?
-
我们的目的是只在组件加载完成的时候获取数据
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function App() {
const [data, setData] = useState({ hits: [] });
// 单独拆分 fetchData
const fetchData = async () => {
const result = await axios(
'http://hn.algolia.com/api/v1/search?query=redux',
);
setData(result.data);
};
// 单独拆分 fetchData 的原因是: 上面的 waring 部分,不推荐把 async 写在 effect 中
useEffect(() => {
fetchData();
}, []);
// 这里的第二个参数: 是 hooks 来观测数值的变化
// 这里添加 [],当我们的组件更新的时候回去观测 effect 的值是否有变化,这里添加空 [] ,是为了防止 hooks 再一次运行。
return (
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
);
}
export default App;
复制代码
3、 Form 表单的时候怎么使用?
- 使用 Form 的时候
function App() {
...
// In order to prevent the default behavior,为了防止form 的默认行为,我们建议单独把 doGet 方法拆分出来写。而不是直接写在 form
的 onchange 方法中
const doGet = event => {
setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`);
event.preventDefault();
};
return (
<Fragment>
<form onSubmit={doGet}>
<input
type="text"
value={query}
onChange={event => setQuery(event.target.value)}
/>
<button type="submit">Search</button>
</form>
{isError && <div>Something went wrong ...</div>}
...
</Fragment>
);
}
复制代码
4、怎么定制一个数据获取的 hook?
- 我们当然可以根据自己的情况定制一个 hook 来获取我们的数据,这里需要处理 loading ,error 情况,数据来源等。
// 做一个新闻的数据获取的 API
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);
const fetchData = async () => {
setIsError(false);
setIsLoading(true);
try {
const result = await axios(url);
setData(result.data);
} catch (error) {
setIsError(true);
}
setIsLoading(false);
};
useEffect(() => {
fetchData();
}, [url]);
const doGet = event => {
setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`);
event.preventDefault();
};
return { data, isLoading, isError, doGet };
}
复制代码
- 怎么使用
// 直接调用即可
function App() {
const [query, setQuery] = useState('redux');
const { data, isLoading, isError, doGet } = useHackerNewsApi();
return (
<Fragment>
...
</Fragment>
);
}
复制代码
进一步的定制 API
的 url
const useHackerNewsApi = () => {
...
useEffect(
...
);
const doGet = (event, url) => {
setUrl(url);
event.preventDefault();
};
return { data, isLoading, isError, doGet };
};
function App() {
const [query, setQuery] = useState('redux');
const { data, isLoading, isError, doGet } = useHackerNewsApi();
return (
<Fragment>
<form
onSubmit={event =>
doGet(
event,
`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>
...
</Fragment>
);
}
复制代码
定制 state
的 初始化data
import React, { Fragment, useState, useEffect } from 'react';
import axios from 'axios';
// 定制 initialUrl 和 initialData
const useDataApi = (initialUrl, initialData) => {
const [data, setData] = useState(initialData);
const [url, setUrl] = useState(initialUrl);
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
const fetchData = async () => {
setIsError(false);
setIsLoading(true);
try {
const result = await axios(url);
setData(result.data);
} catch (error) {
setIsError(true);
}
setIsLoading(false);
};
useEffect(() => {
fetchData();
}, [url]);
const doGet = (event, url) => {
setUrl(url);
event.preventDefault();
};
return { data, isLoading, isError, doGet };
};
function App() {
const [query, setQuery] = useState('redux');
const { data, isLoading, isError, doGet } = useDataApi(
'http://hn.algolia.com/api/v1/search?query=redux',
{ hits: [] },
);
return (
<Fragment>
<form
onSubmit={event =>
doGet(
event,
`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>}
{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;
复制代码
好了现在我们可以优雅地使用 hooks 去获取我们的数据了