React笔记应用:实现已固定笔记的取消固定与归位功能
解决React笔记应用固定状态取消问题:让已固定笔记回到原位置
你的问题核心出在三个地方:
- 用排序后数组的索引定位笔记,导致点击时修改了错误的元素
- 固定数量限制逻辑写在渲染阶段,引发状态不一致
- 未保留笔记的原始创建顺序,取消固定后无法回到原位置
下面是具体修复方案和完整代码:
核心修复点
1. 用唯一ID替代索引定位笔记
渲染时的sortedNotes是排序后的数组,索引和原notes数组完全不匹配,用这个索引去修改状态自然找不对目标笔记。改用每个笔记的id定位,彻底避免索引混乱。
2. 把固定数量限制移到点击事件里
原来在sortedNotes的map中修改pinned状态,属于渲染阶段修改状态,会导致React状态更新异常。把限制逻辑放到togglePin函数里,符合状态更新的正确时机。
3. 添加时间戳保留原始顺序
给每个新笔记添加createdAt时间戳,排序时固定笔记在前,未固定笔记按创建时间升序排列,这样取消固定后笔记会回到它最初的位置。
修改后的完整代码
import React, { useState } from "react"; import "../styles/Notes.css"; import { useToast } from '@chakra-ui/react' import { BsFillPinFill, BsTrashFill, BsPinAngle } from "react-icons/bs"; function NotesComponent() { const [notes, setNotes] = useState([]); const [title, setTitle] = useState(""); const [tagline, setTagline] = useState(""); const [body, setBody] = useState(""); const [page, setPage] = useState(1); const toast = useToast() // 生成唯一ID const generateUniqueNumber = () => { let uniqueNumber = ""; while (uniqueNumber.length < 4) { let digit = Math.floor(Math.random() * 10); if (!uniqueNumber.includes(digit)) { uniqueNumber += digit; } } return uniqueNumber; }; // 提交笔记 const handleSubmit = (event) => { event.preventDefault(); if (!title || !tagline || !body) { toast({ title: '请填写完整内容', status: 'error', duration: 9000, isClosable: true, }); return; } const newNote = { title, tagline, body, pinned: false, id: generateUniqueNumber(), createdAt: Date.now() // 添加创建时间戳,用于维持原始顺序 }; setNotes([...notes, newNote]); setTitle(""); setTagline(""); setBody(""); }; // 切换固定状态 const togglePin = (noteId) => { setNotes(prevNotes => { const targetNote = prevNotes.find(note => note.id === noteId); if (!targetNote) return prevNotes; const pinnedCount = prevNotes.filter(n => n.pinned).length; // 固定数量限制:最多2条 if (!targetNote.pinned && pinnedCount >= 2) { toast({ title: '最多只能固定2条笔记', status: 'warning', duration: 3000, isClosable: true, }); return prevNotes; } // 切换目标笔记的固定状态 return prevNotes.map(note => note.id === noteId ? {...note, pinned: !note.pinned} : note ); }); }; // 排序规则:固定笔记在前,未固定笔记按创建时间升序排列 const sortedNotes = [...notes].sort((a, b) => { if (a.pinned !== b.pinned) { return a.pinned ? -1 : 1; } return a.createdAt - b.createdAt; }); // 分页处理 const pages = [1, 2, 3, 4, 5, 6]; const pageChangeHandler = (e) => { setPage(parseInt(e.target.innerText)); }; // 删除笔记 const deleteHandler = (noteId) => { setNotes(prevNotes => prevNotes.filter(note => note.id !== noteId)); }; return ( <div className="notes-app-container"> <form onSubmit={handleSubmit} className="notes-form"> <input type="text" placeholder="标题" value={title} onChange={(event) => setTitle(event.target.value)} className="notes-input" /> <input type="text" placeholder="副标题" value={tagline} onChange={(event) => setTagline(event.target.value)} className="notes-input" /> <textarea placeholder="内容" value={body} onChange={(event) => setBody(event.target.value)} className="notes-textarea" /> <button type="submit" className="notes-button"> 添加笔记 </button> </form> <div className="enteredNotesMainParent"> {sortedNotes.slice((page - 1) * 6, page * 6).map((note) => ( <div key={note.id} className="enteredNoteIndivitual"> <div>{note.title}</div> <div>{note.tagline}</div> <div>{note.body}</div> <br /> <div className="noteCtaHold"> <div> {/* 根据固定状态切换图标,提升用户感知 */} {note.pinned ? ( <BsFillPinFill className="noteIcon" onClick={() => togglePin(note.id)} /> ) : ( <BsPinAngle className="noteIcon" onClick={() => togglePin(note.id)} /> )} </div> <div> <BsTrashFill className="noteIcon" onClick={() => deleteHandler(note.id)} /> </div> </div> </div> ))} </div> {notes.length === 0 ? <p> 快来添加笔记吧✅ </p> : ""} {notes.length >= 6 && ( <div className="pagesHold"> {pages.map((item) => ( <p key={item} onClick={pageChangeHandler} className={`indivitualPage ${page === item ? 'active' : ''}`} > {item} </p> ))} </div> )} </div> ); } export default NotesComponent;
额外优化说明
- 修正了分页切片的起始索引,避免分页逻辑错误
- 根据固定状态显示不同图标,让用户直观感知当前状态
- 把生成ID的函数独立出来,提升代码可读性
内容的提问来源于stack exchange,提问作者Govind
相关产品推荐
相关产品推荐

