JavaScript >> Javascript 文檔 >  >> React

使用 Hooks 在 React 中構建一個 CRUD 應用程序

React 中引入了一個新概念 - Hooks。 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,看看一個應用程序在沒有任何類的情況下看起來如何,但我還沒有看到任何示例,所以我決定自己製作一個。我創建了一個使用 Hooks 而沒有類的簡單 CRUD(創建、讀取、更新、刪除)應用程序,並且我為其他想要學習如何使用它們的人創建了本教程。

如果你不知道如何在 React 中製作一個簡單的 CRUD 應用,不管你是使用類還是鉤子,這篇文章對你也有好處。

  • 查看演示應用
  • 查看源代碼

先決條件

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

目標

在本教程中,我們將製作一個簡單的 CRUD 應用程序。它將擁有用戶,您將能夠添加、更新或刪除用戶。我們不會使用任何 React 類,而是在功能組件上使用 State Hooks 和 Effect Hooks。如果中途迷路,請務必查看已完成項目的來源。

創建 React 應用

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

npx create-react-app react-hooks

然後運行 npm i .

現在你已經準備好了 React。

初始設置

讓我們從不需要的樣板文件中清除所有文件開始。刪除 /src 中的所有內容 App.js 以外的文件夾 , index.js , 和 index.css .

對於 index.css ,我只是從 Primitive 複製並粘貼 CSS,這是我製作的一個簡單的 CSS 樣板,因為這個應用程序的重點是在 React 上工作,而不關心設計。這個 CSS 樣板只是添加了一些合理的默認值和一個簡單的網格,這樣我們就可以開始原型設計了。

index.js ,我們將通過刪除對 Service Worker 的引用來簡化它。

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

現在我們有了應用程序的初始設置和框架。

狀態與掛鉤狀態

如果我們看一個非常簡單的帶有狀態的類組件和帶有 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 介紹中閱讀所有相關內容。我只是要向你展示如何與他們合作創建一個功能性應用程序。

設置視圖

我們要做的第一件事是為視圖製作一些示例數據和一個表格來顯示它。創建一個名為 tables 的新目錄 在 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

讓我們引入一些隨機的虛擬數據和 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

道具就像以前一樣工作。我們將映射通過我們發送的用戶數據並顯示每個用戶的屬性,或者如果沒有用戶則顯示一條消息。編輯和刪除按鈕還沒有連接到任何東西,所以它們不會做任何事情。

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

稍後我們將介紹編輯和刪除按鈕。現在基本視圖已經設置好了,讓我們開始使用添加功能吧。

添加新用戶

我們將設置表單以添加新用戶。

我們可以做的第一件事是創建將新用戶添加到狀態的實際函數。我們有 setUsersuseState 自動運行 ,所以我們將使用它來更新用戶狀態。

由於我們沒有使用真正的 API 和數據庫,它可能具有自動遞增的 ID,因此我將手動遞增新用戶的 ID。此函數將採用 user 對像作為參數,並將它們添加到 users 對像數組。 ...users 代碼確保所有以前的用戶都保留在數組中。

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

我們將為此創建一個組件,因此我將繼續在頂部添加對組件的引用,並將該組件插入到“添加用戶”標題下方。我們可以通過 addUser() 通過作為道具。當我們將其作為參考傳遞時,請確保不要包含括號 - <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 總是被傳遞到任何 on DOM 中的事件,所以你會看到它作為函數的參數。對象解構將使我們輕鬆獲得 name (鍵)和 value 從表格。最後,我們將像在 App 上一樣設置用戶 組件,除了這次我們使用計算屬性名稱來動態設置名稱(使用 [name] ) 和價值。

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

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

現在我們從狀態對像中提取值,並在 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 函數,我們將阻止觸發默認表單提交。我添加了一點驗證以確保無法提交空值,並將用戶發送到添加功能。最後,我使用setter在提交成功後將表單重置為初始值。

<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

很酷。

刪除用戶

下一個我們要解決的是刪除用戶,這是最容易處理的功能。

低於 addUserApp.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>

現在您可以刪除部分或全部用戶了。

更新用戶

最後一個難題是引入更新現有用戶的能力。這將類似於添加用戶,除了我們必須能夠識別正在編輯的用戶。在類組件中,我們將使用 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 設置狀態 通過道具。還有一個取消按鈕,可以簡單地關閉編輯模式。

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 state 是真還是假。如果為真,則顯示編輯表單。如果為 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>

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

使用效果掛鉤

如果你稍微玩弄一下,你會注意到一個問題。兩個,其實。如果您開始編輯一個用戶,然後嘗試切換到另一個用戶,則不會發生任何事情。為什麼?嗯,組件已經打開了,雖然 parent 上的 state 發生了變化,但它並沒有註冊到 props 中。

這就是 Effect Hook 發揮作用的地方。我們想讓 EditUserForm 組件知道 props 已更改,我們之前使用 componentDidUpdate 會這樣做 .

第一步是引入 useEffect .

EditUserForm.js
import React, { useState, useEffect } from 'react'
EditUserForm.js
useEffect(() => {
  setUser(props.currentUser)
}, [props])

在 Effect Hook 中,我們創建了一個回調函數來更新 user 使用正在發送的新道具進行說明。之前,我們需要比較 if (prevProps.currentUser !== this.state.currentUser) ,但是使用 Effect Hook 我們可以只傳遞 [props] 通過讓它知道我們在看道具。

現在,如果您嘗試更改正在編輯的用戶,它將正常工作!

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

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

結論

我沒有深入介紹 Hooks 的所有用例或所有功能,但我嘗試提供一個完整但簡單的 React 程序的工作示例。有關與鉤子相關的所有內容的完整常見問題解答,請查看鉤子常見問題解答。

如果您在途中迷路了,別忘了查看演示和源代碼。

  • 查看演示應用
  • 查看源代碼

接下來要注意的是 Suspense API。


Tutorial JavaScript 教程
  1. 使用 React + Redux 構建購物車

  2. PBT 2021 的到來 - 第 7 天 - 解決方案

  3. Angular 8.0 的新特性:簡介、特性和優勢

  4. ngRx/Store 和 5 個愚蠢的錯誤

  5. 使用 GraphQL 和 React 的 Feature Flag 方法🕹

  6. 在 React 中模擬和測試 GraphQL

  7. JavaScript 中的數組

  1. Salesforce 開發人員背後的嚴肅堆棧簡介

  2. 角度與反應!

  3. 如何在 AngularJS 的 ng-options 中設置 value 屬性?

  4. 保持理智:每日工作效率

  5. 導航計時 API 包含當前導航的類型

  6. 學習區塊鏈開發#day1 - 作為前端開發人員。

  7. 了解 JavaScript/TypeScript 記憶

  1. Android 中的數據綁定:示例教程

  2. 立即為這 8 個前端網站添加書籤!

  3. 使用 JavaScript 創建漂亮的玫瑰花結圖案🖌️🌼

  4. 在你的 javascript 代碼中使用 js linter 和 strict 指令