使

使用 Hooks 在 React 中创建 CRUD 应用程序 1. 设置项目 2. 添加用户表 3. 添加用户 4. 删除用户 5. 更新用户 6. 使用 Effect Hook 7. 额外奖励:从 API 获取用户

2025-06-07

使用 Hooks 在 React 中创建 CRUD 应用

1. 设置项目

2.添加用户表

3. 添加用户

4.删除用户

5. 更新用户

6. 使用 Effect Hook

7. 额外收获:通过 API 获取用户

在本教程中,我们将使用 React Hooks 构建一个用于创建、读取、更新和删除数据的 Web 应用。Hooks 让我们可以在函数式组件中使用状态和其他功能,而无需编写类组件。

查看演示
查看代码

本教程分为以下几个部分:

  1. 设置项目
  2. 添加用户表
  3. 添加用户
  4. 删除用户
  5. 更新用户
  6. 使用 Effect Hook
  7. 额外奖励:通过 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">
Enter fullscreen mode Exit fullscreen mode

然后将 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
Enter fullscreen mode Exit fullscreen mode

2.添加用户表

我们将从单独的文件中检索用户数据。在 /src 中创建 data.js 文件,并添加一个名为 users 的数组,其中包含几个用户对象,然后将其导出:

const userList = [
    {
        id: 1,
        name: 'Frank',
        username: 'Frank Degrassi'
    },
    {
        id: 2,
        name: 'Birgit',
        username: 'Birgit Boswald'
    }
];

export default userList;
Enter fullscreen mode Exit fullscreen mode

然后创建一个名为 /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;
Enter fullscreen mode Exit fullscreen mode

该表通过用户属性循环遍历 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
Enter fullscreen mode Exit fullscreen mode

确保在 App.js 中导入 UserTable,并将用户作为 props 添加到 UserTable 中。

3. 添加用户

接下来,我们将添加添加用户的功能,首先将该功能添加到 App.js 中,该功能从我们将创建的添加用户组件接收新用户。

addUser 函数将一个包含新用户的对象放入用户对象数组 users 中。我们使用 useState 函数中的 setUsers 来实现这一点。通过使用扩展运算符,我们可以保持当前用户数组不变。我们将根据当前用户数量加一来设置 ID。

const addUser = user => {
    user.id = users.length + 1;
    setUsers([...users, user]);
  }
Enter fullscreen mode Exit fullscreen mode

然后我们将此函数传递给我们的添加用户组件:

<AddUserForm addUser={addUser} />
Enter fullscreen mode Exit fullscreen mode

我们现在就创建它!创建一个文件夹 /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;
Enter fullscreen mode Exit fullscreen mode

我们再次使用 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;
Enter fullscreen mode Exit fullscreen mode

太棒了!现在我们可以添加用户了。注意,在 handleSubmit 函数中,我们阻止了默认页面刷新,并且检查了 user.name 和 user.username 是否都已填写。

更新:为了确保新用户仅在其状态已设置的情况下才会被添加,我们addUser在 handleChange 完成后向该函数传入一个回调。这解决了快速添加同一个用户时出现的 bug。

4.删除用户

现在我们将添加删除用户的功能,这很简单。我们只需过滤用户数组,并筛选出具有我们要删除的用户 ID 的用户。再次使用 setUsers 函数来更新新用户的状态。

UserTable.jsx

<button onClick={() => props.deleteUser(id)}>Delete</button>
Enter fullscreen mode Exit fullscreen mode

App.js

const deleteUser = id => setUsers(users.filter(user => user.id !== id));

<UserTable users={users} deleteUser={deleteUser} />
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

onSubmit 我们将更新的用户发送回 App.js

在 App.js 中,我们将再次使用 useState 函数来检查用户当前是否正在编辑,并确定当前正在编辑哪个用户:

const [editing, setEditing] = useState(false);

const initialUser = {id: null, name: '', username: ''};

const [currentUser, setCurrentUser] = useState(initialUser);
Enter fullscreen mode Exit fullscreen mode

我们将根据编辑状态显示 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>
Enter fullscreen mode Exit fullscreen mode

然后我们将在 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)))
}
Enter fullscreen mode Exit fullscreen mode

太棒了!现在我们可以编辑用户了。下一节我们来修复最后一个问题。

6. 使用 Effect Hook

目前无法在编辑时更改用户,我们可以通过使用 effect hook 来解​​决这个问题。这类似于类组件中的 componentDidMount()。首先,请确保在 EditUserForm.jsx 中导入 useEffect。

useEffect(() => {
    setUser(props.currentUser)
}, [props])
Enter fullscreen mode Exit fullscreen mode

这将使得当组件重新渲染时,道具也会更新。

太棒了!我们已经完成了使用 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);
}
Enter fullscreen mode Exit fullscreen mode

我们只需将其放在async函数前面,然后就可以使用它await来仅在该行代码执行完成后执行下一行代码。我们将结果转换为 JSON,然后将结果打印到屏幕上。我们可以将其放在useEffectApp.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;
Enter fullscreen mode Exit fullscreen mode

这里发生的事情如下。通过这个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]);
Enter fullscreen mode Exit fullscreen mode

这里的变化是,用户现在设置为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>
        )}
Enter fullscreen mode Exit fullscreen mode

感谢你学习本教程!记得关注我,获取更多技巧和窍门。

查看演示
查看代码

文章来源:https://dev.to/sanderdebr/creating-a-crud-app-in-react-with-hooks-3jml
PREV
完美的 React 状态管理:useReducer 和 Context API
NEXT
机器学习 - 简介