React系列教程(3)使用React Hooks开发一个增删改查App

简介: React系列教程(3)使用React Hooks开发一个增删改查App

豆约翰习惯将掌握某一技术分为5个层次:初窥门径,小试牛刀,渐入佳境,得心应手,玩转自如

本篇属于React框架中的第1层次即初窥门径

本文翻译自:

https://www.taniarascia.com/crud-app-in-react-with-hooks/

在React- Hooks中引入了一个新概念。钩子是类的替代方法。如果您以前使用过React,那么您将熟悉简单的(功能性)组件类组件

简单组件

const Example = () => {
  return <div>I'm a simple component</div>
}

类组件

class Example extends Component {
  render() {
    return <div>I'm a class component</div>
  }
}

直到现在,类的许多可用功能(例如生命周期方法状态)才对简单组件可用。新的Hooks提案添加了所有这些功能以及更多功能。

我想尝试一下Hooks,看看没有任何类的应用看起来如何,但是我还没有看到任何示例,所以我决定自己做一个。我创建了一个简单的CRUD(创建,读取,更新,删除)应用程序,该应用程序使用了Hooks,没有使用类,并且为其他想学习如何使用它们的人创建了本教程。

如果您不知道如何在React中制作一个简单的CRUD应用程序,无论您使用类还是钩子,这篇文章也将对您有所帮助。

先决条件


为了遵循本教程,您需要具备HTML,CSS和JavaScript / ES6的基础知识。您还应该了解React的基础知识,可通过阅读React入门来学习。

目标


在本教程中,我们将制作一个简单的CRUD应用程序。它将有用户,您将能够添加,更新或删除用户。我们将不使用任何React类,而是在功能组件上使用状态挂钩和效果挂钩。如果您一路迷路,请务必检查完成项目的来源


创建React应用


我们将从使用create-react-app(CRA)安装项目开始。

npx create-react-app react-hooks

然后运行npm i

现在,您已经准备好使用React。


最初设定


首先,从不需要的样板中清除所有文件。删除一切从/src文件夹除外App.jsindex.jsindex.css

对于index.css,我只是从Primitive复制并粘贴CSS,Primitive是我制作的一个简单CSS样板,因为此应用程序的重点是在React上工作,而不在乎设计。这个CSS样板只是添加了一些合理的默认值和一个简单的网格,因此我们可以开始制作原型。

在中index.js,我们将通过删除对Service Workers的引用来简化它。

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
ReactDOM.render(<App />, document.getElementById('root'))

在中App.js,我将为而制作一个简单的功能组件,App而不是一个类。

App.js

import React from 'react'
const App = () => {
  return (
    <div className="container">
      <h1>CRUD App with Hooks</h1>
      <div className="flex-row">
        <div className="flex-large">
          <h2>Add user</h2>
        </div>
        <div className="flex-large">
          <h2>View users</h2>
        </div>
      </div>
    </div>
  )
}
export default App

现在,我们有了该应用程序的初始设置和框架。


image.png

State vs. Hook State


如果我们看一个非常简单的带有状态的类组件和一个带有Hook状态的功能组件的示例,我们可以看到相同点和不同点。使用类状态,您将获得一个主状态对象,然后使用类和上的方法进行更新setState()

我将快速制作一些示例代码,就好像它是一个图书馆,并且您有带有状态的书一样。

类组件状态示例

class App extends Component {
  initialState = {
    title: '',
    available: false,
  }
  state = initialState
  updateBook = book => {
    this.setState({ title: book.title, available: book.available })
  }
}

有了Hook状态,每种状态类型都有一个getter和setter方法(可以随意设置),并且我们显然创建函数而不是方法。

挂钩状态示例

const App = () => {
  const initialBookState = {
    title: '',
    available: false,
  }
  const [book, setBook] = useState(initialBookState)
  const updateBook = book => {
    setBook({ title: book.title, available: book.available })
  }
}

我不会深入了解钩子与类组件之间的基本原理,因为您可以在React的Hooks简介中了解所有内容。我将向您展示如何与他们一起创建功能实用的应用程序。


设置视图


我们要做的第一件事是为视图创建一些示例数据和一个表格以显示它。创建一个名为tablesin 的新目录src,以及一个名为的文件UserTable.js。我们将制作表格的骨架。

表/UserTable.js

import React from 'react'
const UserTable = () => (
  <table>
    <thead>
      <tr>
        <th>Name</th>
        <th>Username</th>
        <th>Actions</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Name data</td>
        <td>Username data</td>
        <td>
          <button className="button muted-button">Edit</button>
          <button className="button muted-button">Delete</button>
        </td>
      </tr>
    </tbody>
  </table>
)
export default UserTable


现在,只需导入文件并添加新组件即可。

App.js

import React from 'react'
import UserTable from './tables/UserTable'
const App = () => {
  return (
    <div className="container">
      <h1>CRUD App with Hooks</h1>
      <div className="flex-row">
        <div className="flex-large">
          <h2>Add user</h2>
        </div>
        <div className="flex-large">
          <h2>View users</h2>
          <UserTable />
        </div>
      </div>
    </div>
  )
}
export default App


image.png


让我们引入一些随机的虚拟数据和useState从React导入。

App.js

import React, { useState } from 'react'
import UserTable from './tables/UserTable'
const App = () => {
  const usersData = [
    { id: 1, name: 'Tania', username: 'floppydiskette' },
    { id: 2, name: 'Craig', username: 'siliconeidolon' },
    { id: 3, name: 'Ben', username: 'benisphere' },
  ]
  const [users, setUsers] = useState(usersData)
  return (
    <div className="container">
      <h1>CRUD App with Hooks</h1>
      <div className="flex-row">
        <div className="flex-large">
          <h2>Add user</h2>
        </div>
        <div className="flex-large">
          <h2>View users</h2>
          <UserTable users={users} />
        </div>
      </div>
    </div>
  )
}
export default App

Props 的工作原理与以前一样。我们将通过发送的用户数据进行映射,并显示每个用户的属性,如果没有用户,则显示一条消息。编辑和删除按钮尚未连接到任何东西,因此它们不会做任何事情。

UserTable.js

import React from 'react'
const UserTable = props => (
  <table>
    <thead>
      <tr>
        <th>Name</th>
        <th>Username</th>
        <th>Actions</th>
      </tr>
    </thead>
    <tbody>
      {props.users.length > 0 ? (
        props.users.map(user => (
          <tr key={user.id}>
            <td>{user.name}</td>
            <td>{user.username}</td>
            <td>
              <button className="button muted-button">Edit</button>
              <button className="button muted-button">Delete</button>
            </td>
          </tr>
        ))
      ) : (
        <tr>
          <td colSpan={3}>No users</td>
        </tr>
      )}
    </tbody>
  </table>
)
export default UserTable


image.png

稍后我们将介绍编辑和删除按钮。现在已经设置了基本视图,让我们开始添加功能。

添加新用户

我们将设置表单以添加新用户。

我们可以做的第一件事是创建实际功能,该功能会将新用户添加到状态中。我们setUsers自动提供了来自的功能useState,因此我们将使用它来更新用户状态。

由于我们没有使用可能具有自动递增ID的真实API和数据库,因此,我将手动增加新用户的ID。该函数将一个user对象作为参数,并将它们添加到users对象数组中。该...users代码确保所有先前的用户都保留在数组中。

App.js

const addUser = user => {
  user.id = users.length + 1
  setUsers([...users, user])
}

我们将为此创建一个组件,因此,我将继续在顶部添加对该组件的引用,并将该组件插入“添加用户”标题下。我们可以addUser()通过作为Props 。当我们将其作为参考时,请确保不要包括括号- <AddUserForm addUser={addUser} />而不是<AddUserForm addUser={addUser()} />

App.js

import React, { useState } from 'react'
import UserTable from './tables/UserTable'
import AddUserForm from './forms/AddUserForm'
const App = () => {
  const usersData = [
    { id: 1, name: 'Tania', username: 'floppydiskette' },
    { id: 2, name: 'Craig', username: 'siliconeidolon' },
    { id: 3, name: 'Ben', username: 'benisphere' },
  ]
  const [users, setUsers] = useState(usersData)
  const addUser = user => {
    user.id = users.length + 1
    setUsers([...users, user])
  }
  return (
    <div className="container">
      <h1>CRUD App with Hooks</h1>
      <div className="flex-row">
        <div className="flex-large">
          <h2>Add user</h2>
          <AddUserForm addUser={addUser} />
        </div>
        <div className="flex-large">
          <h2>View users</h2>
          <UserTable users={users} />
        </div>
      </div>
    </div>
  )
}
export default App

现在,我们必须创建一个可用于添加新用户的表单。让我们创建一个forms子目录,其中包含一个名为的文件AddUserForm.js

AddUserForm.js

import React, { useState } from 'react'
const AddUserForm = props => {
  return (
    <form>
      <label>Name</label>
      <input type="text" name="name" value="" />
      <label>Username</label>
      <input type="text" name="username" value="" />
      <button>Add new user</button>
    </form>
  )
}
export default AddUserForm

目前,该表单为空,由于我们的值字符串为空,因此您无法向其中添加任何值,提交按钮也不会执行任何操作。

就像以前一样,我们将要设置一些状态,只是该状态只是临时的,以便跟踪添加用户表单中当前的内容。

我将使用这些空值创建一个初始状态,并将用户状态设置为空值。在变量中具有初始状态很有用,因为在提交表单后,我们可以将其返回为初始的空值。

AddUserForm.js

const initialFormState = { id: null, name: '', username: '' }
const [user, setUser] = useState(initialFormState)

现在,我们将创建一个函数来更新表单中的状态。event总是传递给onDOM中的任何事件,因此您将看到它作为函数的参数。对象解构将使我们能够轻松地从表单中获取name(key)value。最后,我们将像在App组件上一样对用户进行设置,除了这次我们使用计算的属性名称来动态设置名称(使用[name])和值。

const handleInputChange = event => {
  const { name, value } = event.target
  setUser({ ...user, [name]: value })
}

如果您不了解正在传递的内容,请尝试console.log(event)在输入处理功能中进行尝试。

现在,我们从状态对象中提取值,并在onChange事件中引用我们的函数。

<form>
  <label>Name</label>
  <input type="text" name="name" value={user.name} onChange={handleInputChange} />
  <label>Username</label>
  <input type="text" name="username" value={user.username} onChange={handleInputChange} />
  <button>Add new user</button>
</form>

最后要注意的是实际上将表单提交回App组件。当我们使用传递函数时props,我们将使用道具来访问该函数。我将编写一个onSubmit函数,我们将防止触发默认表单提交。我添加了一点验证,以确保不能提交空值,并将用户发送到add函数。最后,成功提交后,我将使用设置器将表单重置为其初始值。

<form
  onSubmit={event => {
    event.preventDefault()
    if (!user.name || !user.username) return
    props.addUser(user)
    setUser(initialFormState)
  }}
>

幸运的是,这段代码非常简单,因为我们不必担心异步API调用。

这是我们的全部AddUserForm内容。

AddUserForm.js

import React, { useState } from 'react'
const AddUserForm = props => {
  const initialFormState = { id: null, name: '', username: '' }
  const [user, setUser] = useState(initialFormState)
  const handleInputChange = event => {
    const { name, value } = event.target
    setUser({ ...user, [name]: value })
  }
  return (
    <form
      onSubmit={event => {
        event.preventDefault()
        if (!user.name || !user.username) return
        props.addUser(user)
        setUser(initialFormState)
      }}
    >
      <label>Name</label>
      <input type="text" name="name" value={user.name} onChange={handleInputChange} />
      <label>Username</label>
      <input type="text" name="username" value={user.username} onChange={handleInputChange} />
      <button>Add new user</button>
    </form>
  )
}
export default AddUserForm


image.png

删除用户


我们要解决的下一个问题是删除用户,这是最简单的功能。

addUser中的下面App.js,我们将创建deleteUser,它将获取用户ID并将其从用户数组中过滤出来。

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

我们通过Props将该功能传递给UserTable

<UserTable users={users} deleteUser={deleteUser} />

现在我们要做的UserTable.js就是确保删除按钮调用该函数。

<button onClick={() => props.deleteUser(user.id)} className="button muted-button">
  Delete
</button>

现在,您可以删除部分或全部用户。

image.png


更新用户


难题的最后一步是引入更新现有用户的功能。这类似于添加用户,除了我们必须能够识别正在编辑的用户。在类组件中,我们将使用componentDidUpdate生命周期方法来实现这一点,但是现在我们将使用Effect Hook。该Effect Hook就像是componentDidMountcomponentDidUpdate合并。

我们要构造的方式是,当为用户选择“编辑”操作时,“添加用户”表单将变为“编辑用户”表单,并且将使用所选用户的数据预先填充该表单。您可以取消编辑模式,也可以提交更改,这将更新所选用户并退出编辑模式。

让我们开始。在中App.js,我们要做的第一件事是使状态为是否打开编辑模式。它将开始为false。

App.js

const [editing, setEditing] = useState(false)

由于不知道正在编辑的对象,因此我们将为表单创建初始的空状态,就像添加表单一样。

const initialFormState = { id: null, name: '', username: '' }

我们需要一种查看和更新正在编辑的当前用户的人的方法,因此我们会将空用户应用于currentUser状态。

const [currentUser, setCurrentUser] = useState(initialFormState)

在用户上选择“编辑”后,它应打开编辑模式并设置当前用户,我们将在此editRow功能中执行此操作。

const editRow = user => {
  setEditing(true)
  setCurrentUser({ id: user.id, name: user.name, username: user.username })
}

现在,只需将该函数传递给UserTable我们就可以了deleteUser

<UserTable users={users} editRow={editRow} deleteUser={deleteUser} />

在中UserTable.js,我们将user对象发送过来。

UserTable.js

<button
  onClick={() => {
    props.editRow(user)
  }}
  className="button muted-button"
>
  Edit
</button>

现在我们已完成所有设置-有一个用于编辑模式的开关,以及一个按钮,该按钮将在翻转编辑模式开关的同时使当前用户进入状态。

让我们创建在提交编辑表单时将调用的实际函数。与delete(通过ID筛选出用户)或add(将用户追加到数组)不同,update函数需要映射到数组,并更新与通过的ID相匹配的用户。

这意味着我们将使用两个参数-已更新的用户对象和id-并且将使用三元操作来映射用户并找到我们要更新的参数。

App.js

const updateUser = (id, updatedUser) => {
  setEditing(false)
  setUsers(users.map(user => (user.id === id ? updatedUser : user)))
}

我们只需要自己制作编辑表单即可。

创建forms/EditUserForm.js。大部分将与添加表单相同。到目前为止,唯一的区别是我们将直接currentUser通过props 设置状态。还有一个取消按钮,可以简单地关闭编辑模式。

EditUserForm.js

import React, { useState } from 'react'
const EditUserForm = props => {
  const [user, setUser] = useState(props.currentUser)
  const handleInputChange = event => {
    const { name, value } = event.target
    setUser({ ...user, [name]: value })
  }
  return (
    <form
      onSubmit={event => {
        event.preventDefault()
        props.updateUser(user.id, user)
      }}
    >
      <label>Name</label>
      <input type="text" name="name" value={user.name} onChange={handleInputChange} />
      <label>Username</label>
      <input type="text" name="username" value={user.username} onChange={handleInputChange} />
      <button>Update user</button>
      <button onClick={() => props.setEditing(false)} className="button muted-button">
        Cancel
      </button>
    </form>
  )
}
export default EditUserForm

现在,我们必须将编辑表单放入App.js,并创建一个切换以显示添加或编辑表单。

首先,引入组件。

App.js

import EditUserForm from './forms/EditUserForm'

然后创建切换。我们将使用三元运算来检查editing状态是否为true。如果为true,则显示编辑表单。如果为false,则显示添加表单。确保将我们创建的所有功能传递给编辑组件。

App.js

<div className="flex-large">
  {editing ? (
    <div>
      <h2>Edit user</h2>
      <EditUserForm
        setEditing={setEditing}
        currentUser={currentUser}
        updateUser={updateUser}
      />
    </div>
  ) : (
    <div>
      <h2>Add user</h2>
      <AddUserForm addUser={addUser} />
    </div>
  )}
</div>

好的,因此此时单击“编辑”按钮应该可以切换编辑模式,并且您应该能够更新用户。但是,我们完成了吗?

image.png



使用Effect Hook


如果您稍作尝试,则会注意到一个问题。二,实际上。如果您开始编辑一个用户,然后尝试切换到另一用户,则不会发生任何事情。为什么?好了,该组件已经打开,并且尽管父级上的状态已更改,但尚未注册到props。

这就是Effect Hook的位置。我们想让EditUserForm组件知道道具已经更改,这是我们之前使用所做的componentDidUpdate

第一步是引入useEffect

EditUserForm.js

import React, { useState, useEffect } from 'react'

EditUserForm.js

useEffect(() => {
  setUser(props.currentUser)
}, [props])

在效果挂钩中,我们创建了一个回调函数,该函数user使用正在发送的新道具更新状态。之前,我们需要进行比较if (prevProps.currentUser !== this.state.currentUser),但是有了Effect Hook,我们就可以[props]通过它来告知我们正在观看props。

使用[props]数组类似于使用componentDidUpdate。如果您正在执行类似的一次性事件componentDidMount,则可以传递一个空数组([])。

现在,如果您尝试更改要编辑的用户,它将可以正常工作!

我说这里有两个问题,另一个问题是您可以在当前正在编辑用户的同时删除它。我们可以通过添加setEditing(false)到中的deleteUser函数来解决此问题App.js

就是这样。我们有一个完整的CRUD应用程序,利用React State和Effect Hook

目录
相关文章
|
1月前
|
前端开发 JavaScript API
探究 React Hooks:如何利用全新 API 优化组件逻辑复用与状态管理
本文深入探讨React Hooks的使用方法,通过全新API优化组件逻辑复用和状态管理,提升开发效率和代码可维护性。
|
1月前
|
前端开发
create-react-app 如何使用 less/sass 和 react-css-modules?
本文详细介绍了在 create-react-app 项目中如何使用 less/sass 和 react-css-modules 来管理和应用样式。首先,通过安装相应依赖并配置 webpack 支持 less/sass;接着,介绍如何使用这些预处理器编写样式;最后,讲解了如何配置和使用 react-css-modules 实现样式的模块化管理,以及如何结合使用 less/sass 和 react-css-modules 提高开发效率和代码质量。
36 3
|
1月前
|
前端开发
深入探索React Hooks:从useState到useEffect
深入探索React Hooks:从useState到useEffect
25 3
|
1月前
|
前端开发 JavaScript
深入探索React Hooks:从useState到useEffect
深入探索React Hooks:从useState到useEffect
|
1月前
|
前端开发 JavaScript
React Hooks 深入解析
React Hooks 深入解析
28 0
|
1月前
|
前端开发
React Hooks:从基础到进阶的深入理解
React Hooks:从基础到进阶的深入理解
32 0
|
1月前
|
缓存 前端开发 开发者
深入理解React Hooks,打造高效响应式UI
深入理解React Hooks,打造高效响应式UI
34 0
|
2月前
|
小程序 JavaScript 前端开发
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
770 1
|
5天前
|
JSON 缓存 前端开发
HarmonyOS NEXT 5.0鸿蒙开发一套影院APP(附带源码)
本项目基于HarmonyOS NEXT 5.0开发了一款影院应用程序,主要实现了电影和影院信息的展示功能。应用包括首页、电影列表、影院列表等模块。首页包含轮播图与正在热映及即将上映的电影切换显示;电影列表模块通过API获取电影数据并以网格形式展示,用户可以查看电影详情;影院列表则允许用户选择城市后查看对应影院信息,并支持城市选择弹窗。此外,项目中还集成了Axios用于网络请求,并进行了二次封装以简化接口调用流程,同时添加了请求和响应拦截器来处理通用逻辑。整体代码结构清晰,使用了组件化开发方式,便于维护和扩展。 该简介概括了提供的内容,但请注意实际开发中还需考虑UI优化、性能提升等方面的工作。
49 11
|
3天前
|
前端开发 数据库 UED
uniapp开发,前后端分离的陪玩系统优势,陪玩app功能特点,线上聊天线下陪玩,只要4800
前后端分离的陪玩系统将前端(用户界面)和后端(服务器逻辑)分开开发,前者负责页面渲染与用户交互,后者处理数据并提供接口。该架构提高开发效率、优化用户体验、增强可扩展性和稳定性,降低维护成本,提升安全性。玩家可发布陪玩需求,陪玩人员发布服务信息,支持在线聊天、预约及线下陪玩功能,满足多样化需求。[演示链接](https://www.51duoke.cn/games/?id=7)

热门文章

最新文章