使用React實現(xiàn)一個簡單的待辦事項列表的示例代碼
一、介紹
主要功能
用戶可以添加、編輯和刪除待辦事項。
效果展示

這篇文章我們將詳細講解如何建立一個這樣簡單的列表。
二、編碼
(一)搭建項目、劃分組件
第一步,使用 npm i -g create-react-app 全局安裝腳手架。
第二步,使用 create-react-app staying-to-do 創(chuàng)建一個叫staying-to-do的項目文件,這里的文件名字大家可以自由定義。
注意: 這里自定義的文件名不要包含大寫,不然會報錯。
第三步,進入項目文件夾: cd hello-react
第四步,啟動項目: npm start
第四步完成之后會自動打開一個react頁面,頁面中會有一個一個旋轉的react大loge就算啟動成功了!
第五步 劃分組件。
總所周知,react最重要的就是組件,這里很顯然是添加待辦的頭部、統(tǒng)計和刪除的底部、具體展示的中部列表和列表中的每個小展示條,這四個組件的文件夾分別命名為 Header 、 Footer 、 List 、 Item 。如下圖所示:

每個組件都是由一個 index.jsx 和 index.css 構成,把所有組件都放在一個叫 components 的文件夾中,再把 components 放到文件夾 src 方便以后查找,最后形成的項目目錄應該如下:

當然,我這樣的做法并不是一定的,大家可以根據(jù)自己的思路劃分組件。
(二)分析思路、直接開干
傳值方式
很顯然,這個項目最有練習意義的就是組件之間的傳值,例如Header組件輸入的數(shù)據(jù)要在Item組件中展現(xiàn),也就是要實現(xiàn)兄弟組件之間的傳值。我們都知道利用組件三大屬性中的 state 和 props 就能實現(xiàn)父子組件之間的傳值,但是如何實現(xiàn)兄弟組件之間的傳值呢,或許大家都有學過一些消息訂閱啊hook的方法,但是這里我將教大家用最原始的方法實現(xiàn),或許這不是在世紀開發(fā)中最常用的方法,但一定能在初學之時幫助我們更好的練習react的相關特性。
我的方法是把要使用的數(shù)據(jù)放在所有組件的父親——App組件中,然后利用 state 和 props 傳給子組件,這樣就用父子組件傳值實現(xiàn)了兄弟組件的傳值。使用這個方法,父親組件要預先使用props屬性向子組件傳遞一個函數(shù),子組件在調(diào)用這個函數(shù)獲取父親組件保存的數(shù)據(jù)(也就是俗稱的 狀態(tài)在哪里,操作狀態(tài)的方法就在哪里 )
開始編寫
App.jsx文件中,
第一,用一個 todos 的保存待辦事項,每個元素都是一個對象,每個對象包括待辦事項的序號、待辦事項的名稱和是否完成的標志。
第二,引入要用的組件,例如 import Footer from './components/Footer' 。布局,把組件放在該放的地方。引入全局樣式App.css,里面具體寫啥咱后面再說
第三,分析全局要實現(xiàn)的功能,定義相關的函數(shù),然后把函數(shù)用 props 傳給子組件,供子組件使用。例如 Header 組件有一個輸入回車添加待辦事件的功能,本質(zhì)上就是接受一個新的對象,然后更新狀態(tài)里的 todos ,我們把這個函數(shù) addTodo 在App.jsx中定義好,然后傳遞給子組件使用 <Header addTodo={this.addTodo}/>
所以,整個App.jsx的代碼如下:
import React, { Component } from 'react'
import Header from './components/Header'
import List from './components/List'
import Footer from './components/Footer'
import './App.css'
export default class App extends Component {
//狀態(tài)在哪里,操作狀態(tài)的方法就在哪里
//初始化狀態(tài)
state = {todos:[
{id:'001',name:'吃飯',done:true},
{id:'002',name:'睡覺',done:true},
{id:'003',name:'打代碼',done:false},
{id:'004',name:'看書',done:false}
]}
//addTodo用于添加一個todo,接收的參數(shù)是todo對象
addTodo = (todoObj)=>{
//獲取原todos
const {todos} = this.state
//追加一個todo
const newTodos = [todoObj,...todos]
//更新狀態(tài)
this.setState({todos:newTodos})
}
//updateTodo用于更新一個todo對象
updateTodo = (id,done)=>{
//獲取狀態(tài)中的todos
const {todos} = this.state
//匹配處理數(shù)據(jù)
const newTodos = todos.map((todoObj)=>{
if(todoObj.id === id) return {...todoObj,done}
else return todoObj
})
this.setState({todos:newTodos})
}
//deleteTodo用于刪除一個todo對象
deleteTodo = (id)=>{
//獲取原來的todos
const {todos} = this.state
//刪除指定id的todo對象
const newTodos = todos.filter((todoObj)=>{
return todoObj.id !== id
})
//更新狀態(tài)
this.setState({todos:newTodos})
}
//checkAllTodo用于全選
checkAllTodo = (done)=>{
//獲取原來的todos
const {todos} = this.state
//加工數(shù)據(jù)
const newTodos = todos.map((todoObj)=>{
return {...todoObj,done}
})
//更新狀態(tài)
this.setState({todos:newTodos})
}
//clearAllDone用于清除所有已完成的
clearAllDone = ()=>{
//獲取原來的todos
const {todos} = this.state
//過濾數(shù)據(jù)
const newTodos = todos.filter((todoObj)=>{
return !todoObj.done
})
//更新狀態(tài)
this.setState({todos:newTodos})
}
render() {
const {todos} = this.state
return (
<div className="todo-container">
<div className="todo-wrap">
<Header addTodo={this.addTodo}/>
<List todos={todos} updateTodo={this.updateTodo} deleteTodo={this.deleteTodo}/>
<Footer todos={todos} checkAllTodo={this.checkAllTodo} clearAllDone={this.clearAllDone}/>
</div>
</div>
)
}
}如你所見,這里除了 addTodo 函數(shù),還有 Footer 用于統(tǒng)計數(shù)據(jù)的 checkAllTodo 和清除按鈕要用的 clearAllDone ,還有 Item 要用的更新的 updateTodo 和刪除按鈕要用的 deleteTodo 函數(shù),只不過特殊的是 Item 是 List 的子組件,需要通過 List 獲取罷了。
Header組件中
index.jsx中,定義好一個輸入框之后最重要的就是獲取到輸入的內(nèi)容構建一個對象,然后傳給父組件傳來的 addTodo ,實現(xiàn)這個功能的函數(shù)我們叫 handleKeyUp ,這個函數(shù)在 onKeyUp 按鍵彈起后觸發(fā), event 中的 target.value 和 keyCode 可以用于獲取輸入值和某一案件的鍵值。具體代碼如下:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {nanoid} from 'nanoid'
import './index.css'
export default class Header extends Component {
//對接收的props進行:類型、必要性的限制
static propTypes = {
addTodo:PropTypes.func.isRequired
}
//鍵盤事件的回調(diào)
handleKeyUp = (event)=>{
//解構賦值獲取keyCode,target
const {keyCode,target} = event
//判斷是否是回車按鍵
if(keyCode !== 13) return
//添加的todo名字不能為空
if(target.value.trim() === ''){
alert('輸入不能為空')
return
}
//準備好一個todo對象
const todoObj = {id:nanoid(),name:target.value,done:false}
//將todoObj傳遞給App
this.props.addTodo(todoObj)
//回車后清空輸入
target.value = ''
}
render() {
return (
<div className="todo-header">
<input onKeyUp={this.handleKeyUp} type="text" placeholder="請輸入你的代辦事件名稱,按回車鍵確認"/>
</div>
)
}
}在這里我們對父組件App.jsx傳過來的 addTodo 進行類型限制,這里限制為 func 函數(shù)型,這里我們需要使用 npm install prop-types 額外在下載一個庫prop-types來限制。
在構建對象的時候需要一個唯一的id,這里使用 npm install nanoid 安裝nanoid, nanoid 是一個生成唯一標識符(UUID)的函數(shù)。它使用隨機算法生成短字符串,可以用于為應用程序中的唯一標識符生成惟一的、不可預測的值。
List組件中
index.jsx中要接收到父組件App.jsx傳來的todos,updateTodo,deleteTodo,然后引入子組件 Item ,再根據(jù)todos中的每個對象的id來渲染多少個 Item ,代碼如下:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Item from '../Item'
import './index.css'
export default class List extends Component {
//對接收的props進行:類型、必要性的限制
static propTypes = {
todos:PropTypes.array.isRequired,
updateTodo:PropTypes.func.isRequired,
deleteTodo:PropTypes.func.isRequired,
}
render() {
const {todos,updateTodo,deleteTodo} = this.props
return (
<ul className="todo-main">
{
todos.map( todo =>{
return <Item key={todo.id} {...todo} updateTodo={updateTodo} deleteTodo={deleteTodo}/>
})
}
</ul>
)
}
}Item組件中
Item 組件中有一個很明顯的效果就是經(jīng)過哪一個 Item 哪一個 Item 就有一個高亮的效果,所以這里我們需要定義一個狀態(tài) mouse 來判斷鼠標是否經(jīng)過某一 Item ,默認為 false 不經(jīng)過,因此需要定義一個由鼠標進入和鼠標退出觸發(fā)的方法 handleMouse 來改變鼠標的狀態(tài),index.jsx代碼如下:
import React, { Component } from 'react'
import './index.css'
export default class Item extends Component {
state = {mouse:false} //標識鼠標移入、移出
//鼠標移入、移出的回調(diào)
handleMouse = (flag)=>{
return ()=>{
this.setState({mouse:flag})
}
}
//勾選、取消勾選某一個todo的回調(diào)
handleCheck = (id)=>{
return (event)=>{
this.props.updateTodo(id,event.target.checked)
}
}
//刪除一個todo的回調(diào)
handleDelete = (id)=>{
if(window.confirm('確定刪除嗎?')){
this.props.deleteTodo(id)
}
}
render() {
const {id,name,done} = this.props
const {mouse} = this.state
return (
<li style={{backgroundColor:mouse ? '#ddd' : 'white'}} onMouseEnter={this.handleMouse(true)} onMouseLeave={this.handleMouse(false)}>
<label>
<input type="checkbox" checked={done} onChange={this.handleCheck(id)}/>
<span>{name}</span>
</label>
<button onClick={()=> this.handleDelete(id) } className="btn btn-blue" style={{display:mouse?'block':'none'}}>刪除</button>
</li>
)
}
}每次勾選Item中的框框都要改變父組件App中的todos中對象的done屬性,這個方法叫 handleCheck ,由輸入框的是否改變事件觸發(fā);每次點擊Item末尾的按鈕都會觸發(fā) handleDelete 這個方法,它會把當前Item的id傳給父組件的 deleteTodo 方法,通過這種方法刪除父組件中的todos里的對象。
Footer組件中
待辦事件總數(shù)我們直接用父組件App.jsx傳遞過來的todos的長度來表示, index.jsx代碼如下:
import React, { Component } from 'react'
import './index.css'
export default class Footer extends Component {
//全選checkbox的回調(diào)
handleCheckAll = (event)=>{
this.props.checkAllTodo(event.target.checked)
}
//清除已完成任務的回調(diào)
handleClearAllDone = ()=>{
this.props.clearAllDone()
}
render() {
const {todos} = this.props
//已完成的個數(shù)
const doneCount = todos.reduce((pre,todo)=> pre + (todo.done ? 1 : 0),0)
//總數(shù)
const total = todos.length
return (
<div className="todo-footer">
<label>
<input type="checkbox" onChange={this.handleCheckAll} checked={doneCount === total && total !== 0 ? true : false}/>
</label>
<span>
<span>已完成{doneCount}</span> / 全部{total}
</span>
<button onClick={this.handleClearAllDone} className="btn btn-blue">清除已完成事件</button>
</div>
)
}
}這段代碼中想必大家看不懂的就只有 const doneCount = todos.reduce((pre,todo)=> pre + (todo.done ? 1 : 0),0) 這行,其實這段代碼沒什么了不起的,就是JavaScript中會用到的,下面我詳細講解一下:
todos:是一個任務列表數(shù)組,每個任務都有一個done屬性來表示任務是否已完成。reduce:是一個數(shù)組方法,用于對數(shù)組的每個元素進行累積計算。它接受兩個參數(shù):回調(diào)函數(shù)和初始值。(pre, todo) => pre + (todo.done ? 1 : 0):這是一個回調(diào)函數(shù),用于定義每個元素的累積計算規(guī)則。它接受兩個參數(shù):累積值(pre)和當前元素(todo)。對于每個元素,如果todo.done為真(即任務已完成),則返回pre + 1,否則返回pre。0:這是reduce方法的初始值,用于設置初始的累積值。
通過將每個已完成的任務計算為 1 ,未完成的任務計算為 0 ,然后累加所有元素的計算結果,即可得到已完成任務的數(shù)量。最后,將計算結果賦值給 doneCount 變量。
注意事項
我并沒有把代碼完全展現(xiàn)在文章中,例如每個組件的index.css和全局的App.css,這些是CSS的內(nèi)容,大家就自己變著花樣寫吧。
三、總結
這個小案例我們很好的實現(xiàn)了一個可以添加,刪除和編輯的待辦事項的案例,很好的練習了react的兩大屬性 state 和 props ,學會了 prop-types 和 nanoid 組件庫,可見收獲多多。
以上就是使用React實現(xiàn)一個簡單的待辦事項列表的示例代碼的詳細內(nèi)容,更多關于React實現(xiàn)待辦事項列表的資料請關注腳本之家其它相關文章!
相關文章
React模擬實現(xiàn)Vue的keepAlive功能
Vue中,keep-alive組件可以緩存組件狀態(tài),在路由切換時重新掛載,實現(xiàn)這一功能在React中并不簡單,但我們可以借助一個第三方庫——react-activation 來模擬Vue的keep-alive功能,需要的朋友可以參考下2024-10-10
react?+?vite?+?ts項目中優(yōu)雅使用.svg文件
這篇文章主要為大家介紹了react?+?vite?+?ts項目中優(yōu)雅使用.svg文件,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-08-08
解讀useState第二個參數(shù)的"第二個參數(shù)"
這篇文章主要介紹了useState第二個參數(shù)的"第二個參數(shù)",具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03
react-pdf實現(xiàn)將pdf文件轉為圖片,用于頁面展示
這篇文章主要介紹了react-pdf實現(xiàn)將pdf文件轉為圖片,用于頁面展示問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07
react-beautiful-dnd 實現(xiàn)組件拖拽功能
這篇文章主要介紹了react-beautiful-dnd 實現(xiàn)組件拖拽功能,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-08-08

