十八、useFetch自定义钩子
大约 2 分钟约 728 字
src/App.jsx
(一) import React, { useEffect } from "react";
import StudentList from "./components/StudentList";
import StudentContext from "./store/StudentContext";
import "./App.css";
import useFetch from "./hooks/useFetch";
const App = () => {
const {
data: stuData,
loading,
error,
fetchData,
} = useFetch({
url: "students",
});
// useEffect()参数不能是异步函数
useEffect(() => {
fetchData();
}, [fetchData]);
const loadData = () => {
fetchData();
};
return (
<StudentContext.Provider value={{ fetchData }}>
<div className="app">
<button onClick={loadData}>加载数据</button>
{!loading && !error && <StudentList students={stuData} />}
{loading && <p>数据正在加载中...</p>}
{error && <p>数据加载异常!</p>}
</div>
</StudentContext.Provider>
);
};
export default App;
src/components/Student/index.jsx
(二) import React, { useContext, useState } from "react";
import useFetch from "../../hooks/useFetch";
import StudentContext from "../../store/StudentContext";
import StudentForm from "../StudentForm";
const Student = ({
stu: {
id,
attributes: { name, age, gender, address },
},
stu,
}) => {
/*
props = {
stu: {
id: xxx,
attributes: {
name,age,gender,address
}
}
}
*/
const [editing, setEditing] = useState(false);
const ctx = useContext(StudentContext);
const {
loading,
error,
fetchData: delStu,
} = useFetch(
{
url: `students/${id}`,
method: "delete",
},
ctx.fetchData,
);
const deleteStudent = () => {
delStu();
};
const cancelEdit = () => {
setEditing(false);
};
return (
<>
{!editing && (
<tr>
<td>{name}</td>
<td>{gender}</td>
<td>{age}</td>
<td>{address}</td>
<td>
<button onClick={deleteStudent}>删除</button>
<button onClick={() => setEditing(true)}>修改</button>
</td>
</tr>
)}
{editing && <StudentForm stu={stu} onCancelEdit={cancelEdit} />}
{loading && (
<tr>
<td colSpan={5}>正在删除数据...</td>
</tr>
)}
{error && (
<tr>
<td colSpan={5}>删除失败!</td>
</tr>
)}
</>
);
};
export default Student;
src/components/StudentForm/index.jsx
(三) import React, { useCallback, useContext, useState } from "react";
import useFetch from "../../hooks/useFetch";
import StudentContext from "../../store/StudentContext";
import "./index.css";
const StudentForm = (props) => {
const ctx = useContext(StudentContext);
const {
loading,
error,
fetchData: updateStudent,
} = useFetch(
{
url: props.stu ? `students/${props.stu.id}` : "students",
method: props.stu ? "put" : "post",
},
ctx.fetchData,
);
const [inputData, setInputData] = useState({
name: props.stu ? props.stu.attributes.name : "",
gender: props.stu ? props.stu.attributes.gender : "男",
age: props.stu ? props.stu.attributes.age : 0,
address: props.stu ? props.stu.attributes.address : "",
});
const { name, gender, age, address } = inputData;
const nameChange = (e) => {
setInputData((pre) => ({ ...pre, name: e.target.value }));
};
const genderChange = (e) => {
setInputData((pre) => ({ ...pre, gender: e.target.value }));
};
const ageChange = (e) => {
setInputData((pre) => ({ ...pre, age: +e.target.value }));
};
const addressChange = (e) => {
setInputData((pre) => ({ ...pre, address: e.target.value }));
};
const onSubmitAdd = () => {
updateStudent(inputData);
};
const onSubmitEdit = () => {
updateStudent(inputData);
};
return (
<>
<tr className="student-form">
<td>
<input type="text" value={name} onChange={nameChange} />
</td>
<td>
<select value={gender} onChange={genderChange}>
<option value="男">男</option>
<option value="女">女</option>
</select>
</td>
<td>
<input type="text" value={age} onChange={ageChange} />
</td>
<td>
<input type="text" value={address} onChange={addressChange} />
</td>
<td>
{props.stu && (
<>
<button onClick={() => props.onCancelEdit()}>取消</button>
<button onClick={onSubmitEdit}>确认</button>
</>
)}
{!props.stu && <button onClick={onSubmitAdd}>添加</button>}
</td>
</tr>
{loading && (
<tr>
<td colSpan={5}>添加中...</td>
</tr>
)}
{error && (
<tr>
<td colSpan={5}>添加失败!</td>
</tr>
)}
</>
);
};
export default StudentForm;
src/hooks/useFetch.js
(四) React
中的钩子函数只能在 函数组件 或 自定义钩子 中调用- 当需要将
React
中的钩子函数提取到一个公共区域时,可以使用自定义钩子 - 自定义钩子其实就是一个普通函数,只是名字需要使用
use
开头
import { useCallback, useState } from "react";
/*
reqObj:用来存储请求的参数
url:请求地址
method:请求方法
body:请求体【body在请求时传,获取不到最新的表单数据,应该作为fetchData参数传递】
cb:回调函数,请求发送成功时执行
*/
export default function useFetch(reqObj, cb) {
// 数据
const [data, setData] = useState([]);
// 记录数据是否正在加载
const [loading, setLoading] = useState(false);
// 记录错误信息
const [error, setError] = useState(null);
const fetchData = useCallback(async (data) => {
try {
setLoading(true);
setError(null);
const res = await fetch(`http://localhost:1337/api/${reqObj.url}`, {
method: reqObj.method || "get",
headers: {
"Content-type": "application/json",
},
body: data
? JSON.stringify({
data,
})
: null,
});
if (res.ok) {
const data = await res.json();
setData(data.data);
cb && cb();
} else {
throw new Error("数据加载失败!");
}
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
}, []);
// 设置返回值
return {
data,
loading,
error,
fetchData,
};
}