豆約翰習慣將掌握某一技術(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.js,index.js和index.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

刪除用戶
我們要解決的下一個問題是刪除用戶,這是最簡單的功能。
在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就像是componentDidMount和componentDidUpdate合并。
我們要構(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




