【React_02】使用Axios在React中请求数据
初学React
,为了搞懂如何规范的在react
中请求数据,特此以本文记录一下,方便遗忘后复习。
本文参照此文而来,英文尚可的建议阅读原文!原文更细!写得也更好!
本文主要有以下内容:
- 使用
axios
进行简单的数据获取- 加入状态变量,优化交互体验
- 自定义
hook
进行数据获取- 使用
useReducer
改造请求
重点主要在134点! 本文主要适合于刚接触React的初学者以及不知道如何规范的在React
中获取数据的人。
前置条件:首先准备一个后端接口,其代码如下:
@GetMapping("list.do")
public Map<String,Object> getListData(Integer size) {
if (Objects.isNull(size)){
size = 10;
}
List<Student> resultList = new ArrayList<>();
for (int i = 1; i <= size; i++) {
Student student = new Student();
student.setStuId(i);
student.setAge((int) (Math.random() * 100));
student.setStuName(UUID.randomUUID().toString().split("-")[0]);
resultList.add(student);
}
Map<String, Object> hashMap = new HashMap<>();
hashMap.put("data",resultList);
hashMap.put("status",200);
return hashMap;
}
前端采用react 18.2 + axios
进行环境搭建!在成功搭建环境后,删除掉App.jsx
中的无关代码,删除后的代码如下:
import { useState } from 'react'
import './App.css'
import axios from 'axios'
function App() {
return (
<>
<div>
</div>
</>
)
}
export default App
使用axios进行简单的数据获取
首先修改App.jsx
代码如下:
function App() {
const [data, setData] = useState({
data: []
})
return (
<>
<div>
student name
<ul>
{
data.data.map(student =>{
return (
<li key={student.stuId}>
{student.stuName}
</li>
)
})
}
</ul>
</div>
</>
)
}
首先是使用useEffect
进行数据获取,在这一步需要注意如下:
useEffect()
中的箭头函数不能被async
修饰。useEffect()
的第二个参数的问题
首先是第一点,在app.jsx
中添加如下代码
useEffect(async () => {
const result = await axios("http://127.0.0.1:8080/student/list.do")
setData(result.data)
})
此时项目不能够正常运行,项目会出现如下的报错信息:
react-dom.development.js:86 Warning: useEffect must not return anything besides a function, which is used for clean-up.
It looks like you wrote useEffect(async () => ...) or returned a Promise. Instead, write the async function inside your effect and call it immediately:
useEffect(() => { async function fetchData() { // You can await here const response = await MyAPI.getData(someId); // ... } fetchData(); }, [someId]); // Or [] if effect doesn't need props or state
因此根据错误信息,需要修改我们的写法如下:
useEffect(() => {
const fetchData = async () => {
const result = await axios("http://127.0.0.1:8080/student/list.do")
setData(result.data)
}
fetchData();
})
这样修改之后,项目可以正常运行了!页面可以正常显示数据了!但是细心的你一定发现了如下问题:stuName
一直在变化。这是因为在使用useEffect
进行数据获取时,如果没有指定第二个参数,即他的依赖,则useEffect
在每一次render都会运行,因此就导致了useEffect
的无限循环。
所以此时我们添加第二个参数即可,代码如下。
useEffect(() => {
const fetchData = async () => {
const result = await axios("http://127.0.0.1:8080/student/list.do")
setData(result.data)
}
fetchData();
},[])
传递空数组表示,没有任何依赖,即在mounted
后将不会触发该方法。
如果说当我们需要依赖某个参数时,即某个参数发生改变后,我们需要重新加载数据,此时结合后端接口中的参数size
,在App.jsx
中添加如下代码:
const [size,setSize] = useState(5)
useEffect(() => {
const fetchData = async () => {
const result = await axios(`http://127.0.0.1:8080/student/list.do?size=${size}`)
setData(result.data)
}
fetchData();
}, [size])
<div>
student name
<input
type='number'
value={size}
onChange={event => setSize(event.target.value)}
/>
<ul>
省略其他代码
</ul>
</div>
此时当我们在输入框输入数字时,就会发现内容的改变。但是这样存在了其他问题,每当我们输入一个数时,一位数效果不明显,在输入3位数或者4位数时,每输入一个字符,他都会发出一个http请求。进行数据获取,显然这不是我们要的效果。因此就可以改为手动触发,代码如下:
function App() {
const [size, setSize] = useState(5)
const [query, setQuery] = useState('')
const [data, setData] = useState({
data: []
})
useEffect(() => {
const fetchData = async () => {
const result = await axios(`http://127.0.0.1:8080/student/list.do?size=${size}`)
setData(result.data)
}
fetchData();
}, [query])
return (
<>
<div>
student name
<input
type='number'
value={size}
onChange={event => setSize(event.target.value)}
/>
<button onClick={() => setQuery(size)} >click me</button>
<ul>
{
data.data.map(student => {
return (
<li key={student.stuId}>
{student.stuName}
</li>
)
})
}
</ul>
</div>
</>
)
}
这样就可以通过手动点击查询的方式触发!但这种方式还有可以优化的地方,query
和size
这两个状态变量都用于触发查询功能,且query
就是size
的一个复制,因此可以简化为如下:
function App() {
const [size, setSize] = useState(5)
const [url,setUrl] = useState('http://127.0.0.1:8080/student/list.do?size=5')
const [data, setData] = useState({
data: []
})
useEffect(() => {
const fetchData = async () => {
const result = await axios(url)
setData(result.data)
}
fetchData();
}, [url])
return (
<>
<div>
student name
<input
type='number'
value={size}
onChange={event => setSize(event.target.value)}
/>
<button onClick={() => setUrl(`http://127.0.0.1:8080/student/list.do?size=${size}`)} >click me</button>
<ul>
{
data.data.map(student => {
return (
<li key={student.stuId}>
{student.stuName}
</li>
)
})
}
</ul>
</div>
</>
)
}
到这一步简单的数据请求就结束了!如果只是为了获取数据,则到此就结束了!
加入状态变量,优化数据加载过程
写过vue的都应该了解过在element-plus
中的table
组件都有一个loading
配置项,当进行数据请求时,用于显示当前的数据获取状态。这一部分实现的效果就是如此!
const [isLoading, setIsLoading] = useState(false)
// 省略没有改变的其他代码
useEffect(() => {
const fetchData = async () => {
setIsLoading(true)
const result = await axios(url)
setData(result.data)
setIsLoading(false)
}
fetchData();
}, [url])
{
isLoading ? (<div>数据加载中...</div>) : (
<ul>
{
data.data.map(student => {
return (
<li key={student.stuId}>
{student.stuName}
</li>
)
})
}
</ul>
)
}
然后在我们的Java
代码中添加一个延时代码:
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
重新启动项目之后,在这之后的每一次请求页面就会显示一个数据的加载过程!
除了显示数据的加载过程,我们还可以添加异常处理:代码如下,省略了其他没有改变的代码!
const [errorInfo,setErrorInfo] = useState({
isError:false,
message:''
})
useEffect(() => {
const fetchData = async () => {
setIsLoading(true)
setErrorInfo({
isError:false,
message:"no error",
})
try {
const result = await axios(url)
setData(result.data)
} catch (error) {
setErrorInfo({
isError:true,
message:error.message
})
}
setIsLoading(false)
}
fetchData();
}, [url])
{ errorInfo.isError && (<div>数据加载出错: {errorInfo.message}</div>) }
自定义hook进行数据获取
在上面的代码中,我们都是将所有的代码写在了同一个jsx
文件里面,这样不利于我们进行代码维护【想象一下有很多很多的状态变量,这个文件得多大!】;因此需要将数据请求的逻辑提取出来放入至新建的request.js
文件中,代码如下所示:
// request.js
import { useEffect, useState } from 'react'
import axios from 'axios'
export const loadStudentList = (initUrl, initData) => {
const [url, setUrl] = useState(initUrl)
const [data, setData] = useState(initData)
const [isLoading, setIsLoading] = useState(false)
const [errorInfo, setErrorInfo] = useState({
isError: false,
message: ''
})
useEffect(() => {
const fetchData = async () => {
setIsLoading(true)
setErrorInfo({
isError: false,
message: "no error",
})
try {
const result = await axios(url)
setData(result.data)
} catch (error) {
setErrorInfo({
isError: true,
message: error.message
})
}
setIsLoading(false)
}
fetchData();
}, [url])
return [{ data, isLoading, errorInfo }, setUrl];
}
此时App.jsx
的代码如下所示:
function App() {
const [size,setSize] = useState(5)
const [{data,isLoading,errorInfo}, doFetch] = loadStudentList('http://127.0.0.1:8080/student/list.do?size=5',{
data:[]
})
return (
<>
<div>
student name
<input
type='number'
value={size}
onChange={event => setSize(event.target.value)}
/>
<button onClick={() => doFetch(`http://127.0.0.1:8080/student/list.do?size=${size}`)} >click me</button>
{ errorInfo.isError && (<div>数据加载出错: {errorInfo.message}</div>) }
{
isLoading ? (<div>数据加载中...</div>) : (
<ul>
{
data.data.map(student => {
return (
<li key={student.stuId}>
{student.stuName}
</li>
)
})
}
</ul>
)
}
</div>
</>
)
}
这样前后一对比,就简化了很多很多。即使后面有再多的数据接口按照这种方式,这个文件的内容也不会太多!到这里基本上就可以结束了!不过我们还可以使用useReducer
进一步优化我们的请求!
使用useReducer优化
在上面的代码中errorInfo,isLoading
他们是相关的,但是却是分别维护的,因此为了让其聚合在一起可采用reducer进行优化:
import { useEffect, useState, useReducer } from 'react'
import axios from 'axios'
const dataFetchReducer = (state, action) => {
switch (action.type) {
case 'FETCH_INIT':
return {
...state,
isLoading: true,
errorInfo: {
isError:false,
message:'',
}
};
case 'FETCH_SUCCESS':
return {
...state,
isLoading: false,
errorInfo: {
isError:false,
message:'',
},
data: action.payload,
};
case 'FETCH_FAILURE':
return {
...state,
isLoading: false,
errorInfo: {
isError:true,
message:action.payload.message,
}
};
default:
throw new Error();
}
}
export const loadStudentList = (initUrl, initData) => {
const [url, setUrl] = useState(initUrl)
const [state, dispatch] = useReducer(dataFetchReducer, {
isLoading: false,
errorInfo: {
isError: false,
message: ''
},
data: initData,
});
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',payload:error });
}
// setIsLoading(false)
}
fetchData();
}, [url])
return [state, setUrl]
}
如果没有了解过redux
则推荐以下几篇比较好的blog
,不得不说老外的文档写的确实更让人能够理解,尤其是对于初学者而言!
本文的参考资料:
转载自:https://juejin.cn/post/7281560175675408424