React系列教程(3)使用React Hooks開發(fā)一個增刪改查App

豆約翰習慣將掌握某一技術(shù)分為5個層次:初窺門徑,小試牛刀,漸入佳境,得心應手,玩轉(zhuǎn)自如

本篇屬于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>
  }
}

直到現(xiàn)在,類的許多可用功能(例如生命周期方法狀態(tài))才對簡單組件可用。新的Hooks提案添加了所有這些功能以及更多功能。

我想嘗試一下Hooks,看看沒有任何類的應用看起來如何,但是我還沒有看到任何示例,所以我決定自己做一個。我創(chuàng)建了一個簡單的CRUD(創(chuàng)建,讀取,更新,刪除)應用程序,該應用程序使用了Hooks,沒有使用類,并且為其他想學習如何使用它們的人創(chuàng)建了本教程。

如果您不知道如何在React中制作一個簡單的CRUD應用程序,無論您使用類還是鉤子,這篇文章也將對您有所幫助。

先決條件

為了遵循本教程,您需要具備HTML,CSS和JavaScript / ES6的基礎(chǔ)知識。您還應該了解React的基礎(chǔ)知識,可通過閱讀React入門來學習。

目標

在本教程中,我們將制作一個簡單的CRUD應用程序。它將有用戶,您將能夠添加,更新或刪除用戶。我們將不使用任何React類,而是在功能組件上使用狀態(tài)掛鉤和效果掛鉤。如果您一路迷路,請務必檢查完成項目的來源。

創(chuàng)建React應用

我們將從使用create-react-app(CRA)安裝項目開始。

npx create-react-app react-hooks

然后運行npm i。

現(xiàn)在,您已經(jīng)準備好使用React。

最初設(shè)定

首先,從不需要的樣板中清除所有文件。刪除一切從/src文件夾除外App.jsindex.jsindex.css。

對于index.css,我只是從Primitive復制并粘貼CSS,Primitive是我制作的一個簡單CSS樣板,因為此應用程序的重點是在React上工作,而不在乎設(shè)計。這個CSS樣板只是添加了一些合理的默認值和一個簡單的網(wǎng)格,因此我們可以開始制作原型。

在中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

現(xiàn)在,我們有了該應用程序的初始設(shè)置和框架。

State vs. Hook State

如果我們看一個非常簡單的帶有狀態(tài)的類組件和一個帶有Hook狀態(tài)的功能組件的示例,我們可以看到相同點和不同點。使用類狀態(tài),您將獲得一個主狀態(tài)對象,然后使用類和上的方法進行更新setState()。

我將快速制作一些示例代碼,就好像它是一個圖書館,并且您有帶有狀態(tài)的書一樣。

類組件狀態(tài)示例

class App extends Component {
  initialState = {
    title: '',
    available: false,
  }

  state = initialState

  updateBook = book => {
    this.setState({ title: book.title, available: book.available })
  }
}

有了Hook狀態(tài),每種狀態(tài)類型都有一個getter和setter方法(可以隨意設(shè)置),并且我們顯然創(chuàng)建函數(shù)而不是方法。

掛鉤狀態(tài)示例

const App = () => {
  const initialBookState = {
    title: '',
    available: false,
  }

  const [book, setBook] = useState(initialBookState)

  const updateBook = book => {
    setBook({ title: book.title, available: book.available })
  }
}

我不會深入了解鉤子與類組件之間的基本原理,因為您可以在React的Hooks簡介中了解所有內(nèi)容。我將向您展示如何與他們一起創(chuàng)建功能實用的應用程序。

設(shè)置視圖

我們要做的第一件事是為視圖創(chuàng)建一些示例數(shù)據(jù)和一個表格以顯示它。創(chuàng)建一個名為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

現(xiàn)在,只需導入文件并添加新組件即可。

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

讓我們引入一些隨機的虛擬數(shù)據(jù)和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 的工作原理與以前一樣。我們將通過發(fā)送的用戶數(shù)據(jù)進行映射,并顯示每個用戶的屬性,如果沒有用戶,則顯示一條消息。編輯和刪除按鈕尚未連接到任何東西,因此它們不會做任何事情。

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

稍后我們將介紹編輯和刪除按鈕。現(xiàn)在已經(jīng)設(shè)置了基本視圖,讓我們開始添加功能。

添加新用戶

我們將設(shè)置表單以添加新用戶。

我們可以做的第一件事是創(chuàng)建實際功能,該功能會將新用戶添加到狀態(tài)中。我們setUsers自動提供了來自的功能useState,因此我們將使用它來更新用戶狀態(tài)。

由于我們沒有使用可能具有自動遞增ID的真實API和數(shù)據(jù)庫,因此,我將手動增加新用戶的ID。該函數(shù)將一個user對象作為參數(shù),并將它們添加到users對象數(shù)組中。該...users代碼確保所有先前的用戶都保留在數(shù)組中。

App.js

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

我們將為此創(chuàng)建一個組件,因此,我將繼續(xù)在頂部添加對該組件的引用,并將該組件插入“添加用戶”標題下。我們可以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

現(xiàn)在,我們必須創(chuàng)建一個可用于添加新用戶的表單。讓我們創(chuàng)建一個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

目前,該表單為空,由于我們的值字符串為空,因此您無法向其中添加任何值,提交按鈕也不會執(zhí)行任何操作。

就像以前一樣,我們將要設(shè)置一些狀態(tài),只是該狀態(tài)只是臨時的,以便跟蹤添加用戶表單中當前的內(nèi)容。

我將使用這些空值創(chuàng)建一個初始狀態(tài),并將用戶狀態(tài)設(shè)置為空值。在變量中具有初始狀態(tài)很有用,因為在提交表單后,我們可以將其返回為初始的空值。

AddUserForm.js

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

現(xiàn)在,我們將創(chuàng)建一個函數(shù)來更新表單中的狀態(tài)。event總是傳遞給onDOM中的任何事件,因此您將看到它作為函數(shù)的參數(shù)。對象解構(gòu)將使我們能夠輕松地從表單中獲取name(key)value。最后,我們將像在App組件上一樣對用戶進行設(shè)置,除了這次我們使用計算的屬性名稱來動態(tài)設(shè)置名稱(使用[name])和值。

const handleInputChange = event => {
  const { name, value } = event.target

  setUser({ ...user, [name]: value })
}

如果您不了解正在傳遞的內(nèi)容,請嘗試console.log(event)在輸入處理功能中進行嘗試。

現(xiàn)在,我們從狀態(tài)對象中提取值,并在onChange事件中引用我們的函數(shù)。

<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組件。當我們使用傳遞函數(shù)時props,我們將使用道具來訪問該函數(shù)。我將編寫一個onSubmit函數(shù),我們將防止觸發(fā)默認表單提交。我添加了一點驗證,以確保不能提交空值,并將用戶發(fā)送到add函數(shù)。最后,成功提交后,我將使用設(shè)置器將表單重置為其初始值。

<form
  onSubmit={event => {
    event.preventDefault()
    if (!user.name || !user.username) return

    props.addUser(user)
    setUser(initialFormState)
  }}
>

幸運的是,這段代碼非常簡單,因為我們不必擔心異步API調(diào)用。

這是我們的全部AddUserForm內(nèi)容。

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,我們將創(chuàng)建deleteUser,它將獲取用戶ID并將其從用戶數(shù)組中過濾出來。

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

我們通過Props將該功能傳遞給UserTable。

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

現(xiàn)在我們要做的UserTable.js就是確保刪除按鈕調(diào)用該函數(shù)。

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

現(xiàn)在,您可以刪除部分或全部用戶。

更新用戶

難題的最后一步是引入更新現(xiàn)有用戶的功能。這類似于添加用戶,除了我們必須能夠識別正在編輯的用戶。在類組件中,我們將使用componentDidUpdate生命周期方法來實現(xiàn)這一點,但是現(xiàn)在我們將使用Effect Hook。該Effect Hook就像是componentDidMountcomponentDidUpdate合并。

我們要構(gòu)造的方式是,當為用戶選擇“編輯”操作時,“添加用戶”表單將變?yōu)椤熬庉嬘脩簟北韱?,并且將使用所選用戶的數(shù)據(jù)預先填充該表單。您可以取消編輯模式,也可以提交更改,這將更新所選用戶并退出編輯模式。

讓我們開始。在中App.js,我們要做的第一件事是使狀態(tài)為是否打開編輯模式。它將開始為false。

App.js

const [editing, setEditing] = useState(false)

由于不知道正在編輯的對象,因此我們將為表單創(chuàng)建初始的空狀態(tài),就像添加表單一樣。

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

我們需要一種查看和更新??正在編輯的當前用戶的人的方法,因此我們會將空用戶應用于currentUser狀態(tài)。

const [currentUser, setCurrentUser] = useState(initialFormState)

在用戶上選擇“編輯”后,它應打開編輯模式并設(shè)置當前用戶,我們將在此editRow功能中執(zhí)行此操作。

const editRow = user => {
  setEditing(true)

  setCurrentUser({ id: user.id, name: user.name, username: user.username })
}

現(xiàn)在,只需將該函數(shù)傳遞給UserTable我們就可以了deleteUser。

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

在中UserTable.js,我們將user對象發(fā)送過來。

UserTable.js

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

現(xiàn)在我們已完成所有設(shè)置-有一個用于編輯模式的開關(guān),以及一個按鈕,該按鈕將在翻轉(zhuǎn)編輯模式開關(guān)的同時使當前用戶進入狀態(tài)。

讓我們創(chuàng)建在提交編輯表單時將調(diào)用的實際函數(shù)。與delete(通過ID篩選出用戶)或add(將用戶追加到數(shù)組)不同,update函數(shù)需要映射到數(shù)組,并更新與通過的ID相匹配的用戶。

這意味著我們將使用兩個參數(shù)-已更新的用戶對象和id-并且將使用三元操作來映射用戶并找到我們要更新的參數(shù)。

App.js

const updateUser = (id, updatedUser) => {
  setEditing(false)

  setUsers(users.map(user => (user.id === id ? updatedUser : user)))
}

我們只需要自己制作編輯表單即可。

創(chuàng)建forms/EditUserForm.js。大部分將與添加表單相同。到目前為止,唯一的區(qū)別是我們將直接currentUser通過props 設(shè)置狀態(tài)。還有一個取消按鈕,可以簡單地關(guān)閉編輯模式。

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

現(xiàn)在,我們必須將編輯表單放入App.js,并創(chuàng)建一個切換以顯示添加或編輯表單。

首先,引入組件。

App.js

import EditUserForm from './forms/EditUserForm'

然后創(chuàng)建切換。我們將使用三元運算來檢查editing狀態(tài)是否為true。如果為true,則顯示編輯表單。如果為false,則顯示添加表單。確保將我們創(chuàng)建的所有功能傳遞給編輯組件。

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>

好的,因此此時單擊“編輯”按鈕應該可以切換編輯模式,并且您應該能夠更新用戶。但是,我們完成了嗎?

使用Effect Hook

如果您稍作嘗試,則會注意到一個問題。二,實際上。如果您開始編輯一個用戶,然后嘗試切換到另一用戶,則不會發(fā)生任何事情。為什么?好了,該組件已經(jīng)打開,并且盡管父級上的狀態(tài)已更改,但尚未注冊到props。

這就是Effect Hook的位置。我們想讓EditUserForm組件知道道具已經(jīng)更改,這是我們之前使用所做的componentDidUpdate

第一步是引入useEffect。

EditUserForm.js

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

EditUserForm.js

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

在效果掛鉤中,我們創(chuàng)建了一個回調(diào)函數(shù),該函數(shù)user使用正在發(fā)送的新道具更新狀態(tài)。之前,我們需要進行比較if (prevProps.currentUser !== this.state.currentUser),但是有了Effect Hook,我們就可以[props]通過它來告知我們正在觀看props。

使用[props]數(shù)組類似于使用componentDidUpdate。如果您正在執(zhí)行類似的一次性事件componentDidMount,則可以傳遞一個空數(shù)組([])。

現(xiàn)在,如果您嘗試更改要編輯的用戶,它將可以正常工作!

我說這里有兩個問題,另一個問題是您可以在當前正在編輯用戶的同時刪除它。我們可以通過添加setEditing(false)到中的deleteUser函數(shù)來解決此問題App.js。

就是這樣。我們有一個完整的CRUD應用程序,利用React State和Effect Hook

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容