使用 Hooks 在 React 中创建 CRUD 应用
1. 设置项目
2.添加用户表
3. 添加用户
4.删除用户
5. 更新用户
6. 使用 Effect Hook
7. 额外收获:通过 API 获取用户
在本教程中,我们将使用 React Hooks 构建一个用于创建、读取、更新和删除数据的 Web 应用。Hooks 让我们可以在函数式组件中使用状态和其他功能,而无需编写类组件。
本教程分为以下几个部分:
- 设置项目
- 添加用户表
- 添加用户
- 删除用户
- 更新用户
- 使用 Effect Hook
- 额外奖励:通过 API 获取用户
1. 设置项目
我们将首先使用 npm 创建一个 React 应用程序:
npx create-react-app react-crud-hooks
然后浏览到此文件夹并删除 /src 文件夹中除 App.js、index.js 和 index.css 之外的所有内容
对于 index.css,我们将使用一个名为 Skeleton 的简单 CSS 样板,您可以在此处找到:http://getskeleton.com/
将/public文件夹中的样式添加到index.html中:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css">
然后将 App.js 转换为函数组件,并添加以下设置。注意,这个 CSS 样板代码的框架运行起来有多么简单:
import React from 'react'
const App = () => {
return (
<div className="container">
<h1>React CRUD App with Hooks</h1>
<div className="row">
<div className="five columns">
<h2>Add user</h2>
</div>
<div className="seven columns">
<h2>View users</h2>
</div>
</div>
</div>
)
}
export default App
2.添加用户表
我们将从单独的文件中检索用户数据。在 /src 中创建 data.js 文件,并添加一个名为 users 的数组,其中包含几个用户对象,然后将其导出:
const userList = [
{
id: 1,
name: 'Frank',
username: 'Frank Degrassi'
},
{
id: 2,
name: 'Birgit',
username: 'Birgit Boswald'
}
];
export default userList;
然后创建一个名为 /tables 的文件夹,并添加一个 UserTable.jsx 文件。在这里,我们将添加一个循环遍历用户的基本表格。注意,我们使用了三元运算符,它与 if/else 语句相同,会立即返回。此外,我们还解构了对象属性,因此无需再次重写属性。如果没有找到用户,我们将显示一个包含一些文本的空单元格。
import React from 'react';
const UserTable = (props) => {
return (
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Username</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{ props.users.length > 0 ? (
props.users.map(user => {
const {id, name, username} = user;
return (
<tr>
<td>{id}</td>
<td>{name}</td>
<td>{username}</td>
<td>
<button>Delete</button>
<button>Edit</button>
</td>
</tr>
)
})
) : (
<tr>
<td colSpan={4}>No users found</td>
</tr>
)
}
</tbody>
</table>
)
}
export default UserTable;
该表通过用户属性循环遍历 App.js 收到的用户。让我们将它们添加到 App.js 中,并添加从 data.js 中检索用户的功能,这将通过 useState 实现。每个 useState 都有一个 getter 和一个 setter。
import React, {useState} from 'react'
import userList from './data.js';
import UserTable from './tables/UserTable';
const App = () => {
const [users, setUsers] = useState(userList);
return (
<div className="container">
<h1>React CRUD App with Hooks</h1>
<div className="row">
<div className="six columns">
<h2>Add user</h2>
</div>
<div className="six columns">
<h2>View users</h2>
<UserTable users={users} />
</div>
</div>
</div>
)
}
export default App
确保在 App.js 中导入 UserTable,并将用户作为 props 添加到 UserTable 中。
3. 添加用户
接下来,我们将添加添加用户的功能,首先将该功能添加到 App.js 中,该功能从我们将创建的添加用户组件接收新用户。
addUser 函数将一个包含新用户的对象放入用户对象数组 users 中。我们使用 useState 函数中的 setUsers 来实现这一点。通过使用扩展运算符,我们可以保持当前用户数组不变。我们将根据当前用户数量加一来设置 ID。
const addUser = user => {
user.id = users.length + 1;
setUsers([...users, user]);
}
然后我们将此函数传递给我们的添加用户组件:
<AddUserForm addUser={addUser} />
我们现在就创建它!创建一个文件夹 /forms,其中包含一个名为 AddUserForm.jsx 的文件。
import React, {useState} from 'react';
const AddUserForm = (props) => {
const initUser = {id: null, name: '', username: ''};
const [user, setUser] = useState(initUser);
return (
<form>
<label>Name</label>
<input className="u-full-width" type="text" name=”name” value={user.name} />
<label>Username</label>
<input className="u-full-width" type="text" name=”username” value={user.username} />
<button className="button-primary" type="submit">Add user</button>
</form>
)
}
export default AddUserForm;
我们再次使用 useState 来管理新用户的状态。用户值的初始状态为空。现在我们将添加 onChange 和 onSubmit 函数。对于 handleChange,我们解构 event.target 对象的属性。然后,我们根据使用的输入字段动态设置对象键:
import React, {useState} from 'react';
const AddUserForm = (props) => {
const initUser = {id: null, name: '', username: ''};
const [user, setUser] = useState(initUser);
const handleChange = e => {
const {name, value} = e.target;
setUser({...user, [name]: value});
}
const handleSubmit = e => {
e.preventDefault();
if (user.name && user.username) {
handleChange(e, props.addUser(user));
}
}
return (
<form>
<label>Name</label>
<input className="u-full-width" type="text" value={user.name} name="name" onChange={handleChange} />
<label>Username</label>
<input className="u-full-width" type="text" value={user.username} name="username" onChange={handleChange} />
<button className="button-primary" type="submit" onClick={handleSubmit} >Add user</button>
</form>
)
}
export default AddUserForm;
太棒了!现在我们可以添加用户了。注意,在 handleSubmit 函数中,我们阻止了默认页面刷新,并且检查了 user.name 和 user.username 是否都已填写。
更新:为了确保新用户仅在其状态已设置的情况下才会被添加,我们addUser
在 handleChange 完成后向该函数传入一个回调。这解决了快速添加同一个用户时出现的 bug。
4.删除用户
现在我们将添加删除用户的功能,这很简单。我们只需过滤用户数组,并筛选出具有我们要删除的用户 ID 的用户。再次使用 setUsers 函数来更新新用户的状态。
UserTable.jsx
<button onClick={() => props.deleteUser(id)}>Delete</button>
App.js
const deleteUser = id => setUsers(users.filter(user => user.id !== id));
<UserTable users={users} deleteUser={deleteUser} />
5. 更新用户
更新用户比添加或删除用户稍微复杂一些。首先,我们需要在 ./forms/EditUserForm.jsx 中设置表单,并将其导入 App.js。我们只需复制 AddUserForm.jsx 文件,并将 currentUser 更改为从 App.js 获取的用户信息即可:
import React, {useState} from 'react';
const EditUserForm = (props) => {
const [user, setUser] = useState(props.currentUser);
const handleChange = e => {
const {name, value} = e.target;
setUser({...user, [name]: value});
}
const handleSubmit = e => {
e.preventDefault();
if (user.name && user.username) props.updateUser(user);
}
return (
<form>
<label>Name</label>
<input className="u-full-width" type="text" value={user.name} name="name" onChange={handleChange} />
<label>Username</label>
<input className="u-full-width" type="text" value={user.username} name="username" onChange={handleChange} />
<button className="button-primary" type="submit" onClick={handleSubmit} >Edit user</button>
<button type="submit" onClick={() => props.setEditing(false)} >Cancel</button>
</form>
)
}
export default EditUserForm;
onSubmit 我们将更新的用户发送回 App.js
在 App.js 中,我们将再次使用 useState 函数来检查用户当前是否正在编辑,并确定当前正在编辑哪个用户:
const [editing, setEditing] = useState(false);
const initialUser = {id: null, name: '', username: ''};
const [currentUser, setCurrentUser] = useState(initialUser);
我们将根据编辑状态显示 AddUser 或 EditUser 表单:
<div className="container">
<h1>React CRUD App with Hooks</h1>
<div className="row">
<div className="five columns">
{ editing ? (
<div>
<h2>Edit user</h2>
<EditUserForm
currentUser={currentUser}
setEditing={setEditing}
updateUser={updateUser}
/>
</div>
) : (
<div>
<h2>Add user</h2>
<AddUserForm addUser={addUser} />
</div>
)}
</div>
<div className="seven columns">
<h2>View users</h2>
<UserTable users={users} deleteUser={deleteUser} editUser={editUser} />
</div>
</div>
</div>
然后我们将在 App.js 中添加 editUser 和 updateUser 函数:
const editUser = (id, user) => {
setEditing(true);
setCurrentUser(user);
}
const updateUser = (newUser) => {
setUsers(users.map(user => (user.id === currentUser.id ? newUser : user)))
}
太棒了!现在我们可以编辑用户了。下一节我们来修复最后一个问题。
6. 使用 Effect Hook
目前无法在编辑时更改用户,我们可以通过使用 effect hook 来解决这个问题。这类似于类组件中的 componentDidMount()。首先,请确保在 EditUserForm.jsx 中导入 useEffect。
useEffect(() => {
setUser(props.currentUser)
}, [props])
这将使得当组件重新渲染时,道具也会更新。
太棒了!我们已经完成了使用 Hooks 构建 React CRUD 应用。
7. 额外收获:通过 API 获取用户
目前,我们的数据存储在一个普通的 JS 文件中,但大多数情况下,您需要从外部数据源/API 获取数据。在本附加章节中,我们将构建一个异步获取数据源的函数。
让我们使用这个免费的 API 来获取三个随机用户:
https: //randomuser.me/api/?results=3
获取异步数据非常简单,我们可以使用多种解决方案,例如:
- 使用类似 axios 的库
- 使用承诺
- 使用 async/await(更简洁的承诺书写风格)。
我喜欢使用 async await 方法。它如下所示:
const fetchData = async (amount) => {
const response = await fetch(`https://randomuser.me/api/?results=${amount}`);
const json = await response.json();
console.log(json);
}
我们只需将其放在async
函数前面,然后就可以使用它await
来仅在该行代码执行完成后执行下一行代码。我们将结果转换为 JSON,然后将结果打印到屏幕上。我们可以将其放在useEffect
App.js 的钩子中,并在组件挂载时获取数据,但让我们更进一步。
我们将通过将上面的代码放在单独的文件中,然后返回结果和加载状态来创建我们自己的自定义 React Hook。
使用以下代码创建一个名为的hooks
文件的新文件夹:useAsyncRequest.js
import {useState, useEffect} from 'react';
const useAsyncRequest = amount => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(`https://randomuser.me/api/?results=${amount}`);
const json = await response.json();
setData(json.results, setLoading(false));
} catch (err) {
console.warn("Something went wrong fetching the API...", err);
setLoading(false);
}
}
if (amount) {
fetchData(amount);
}
}, [amount]);
return [data, loading]
}
export default useAsyncRequest;
这里发生的事情如下。通过这个useEffect
钩子,我们在页面加载时从 API 中获取数据。每当金额发生变化时,这个函数都会触发,所以只会触发一次,因为我们的金额是一个固定的数字(在我的例子中是 3)。
我添加了一个 try-catch 块来为异步 await 请求添加错误处理。然后我们将返回两个状态变量:data 和loading。我们将在 App 组件中使用它们。
将此文件导入 App 组件内并添加以下内容:
const [data, loading] = useAsyncRequest(3);
const [users, setUsers] = useState(null);
useEffect(() => {
if (data) {
const formattedUsers = data.map((obj, i) => {
return {
id: i,
name: obj.name.first,
username: obj.name.first + " " + obj.name.last,
};
});
setUsers(formattedUsers);
}
}, [data]);
这里的变化是,用户现在设置为null
默认值,并且一旦我们的 Hook 返回结果,我们就会将用户设置为获取的用户。
我们返回的数据不适合我们的 userTable 组件,所以我们必须格式化结果。我在这里通过映射数组来实现,并为每个对象返回一个可以在应用中使用的新对象。
每次变量发生变化时,这个useEffect
函数/钩子都会被触发data
。所以,基本上只要 useAsyncRequest 钩子准备好获取数据,它就会触发。很酷吧!
最后,我们将更新我们的 App 组件,以便它仅在未加载且实际有用户时才呈现用户表:
{loading || !users ? (
<p>Loading...</p>
) : (
<div className="seven columns">
<h2>View users</h2>
<UserTable
users={users}
deleteUser={deleteUser}
editUser={editUser}
/>
</div>
)}
感谢你学习本教程!记得关注我,获取更多技巧和窍门。
文章来源:https://dev.to/sanderdebr/creating-a-crud-app-in-react-with-hooks-3jml