React Context列表修改无更新问题求助:删除异常、搜索后恢复
解决数组CRUD列表删除异常问题
问题现象
实现基于数组的简单CRUD列表时,删除元素总是移除最后一项,不过在搜索栏输入内容后列表会恢复正常。已经尝试用扩展运算符避免直接修改原数组,但问题依旧。
问题根源
使用数组索引作为React组件key
React依赖key属性识别组件唯一性。当数组元素被删除时,索引会重新排列,导致React错误复用组件实例,显示内容与实际数组数据不匹配。Card组件本地状态未同步上下文更新
Card组件将传入的props.name存在本地state中,上下文的items数组更新后,本地state不会自动同步,只有搜索触发App重新渲染时才会更新显示。editItem函数逻辑错误
上下文的editItem参数顺序颠倒,且map逻辑错误,会把未匹配的项也修改成旧值,导致编辑功能异常。
修复步骤
1. 替换组件key为唯一标识
将App.tsx中渲染Card时的key={index}改为key={name}(如果后续有重复名称,建议改用带唯一ID的对象数组,比如{id: string, name: string})。
2. 同步Card组件状态与props
移除Card组件中多余的本地name state,直接使用props.name,并通过useEffect监听props变化更新编辑输入框的值。
3. 修正editItem函数逻辑
调整参数顺序,修正map替换逻辑,确保只有匹配项才会被修改。
修正后的完整代码
App.tsx
import './App.css' import { ItemsContext } from './context/Items'; import { useState, useContext } from 'react'; import { FaSearch } from "react-icons/fa"; import { IoIosAddCircle } from "react-icons/io"; import Card from "./components/Card"; function App() { const [search, setSearch] = useState<string>("") const [addInput, setAddInput] = useState<string>("") const context = useContext(ItemsContext) const addItems = () => { context?.addItem(addInput) setAddInput("") } return ( <main> <nav> <input id='searchInput' type="text" value={search} onChange={e => setSearch(e.target.value)} /> <FaSearch className="inputButton" /> </nav> <section id='cardBox'> {context && context.items?.map((name: string) => { if (name.toLowerCase().includes(search.toLowerCase()) || search === ""){ return <Card key={name} name={name}/> } return null })} </section> <div id='inputFooter'> <input id='addInput' type="text" value={addInput} onChange={e => setAddInput(e.target.value)} /> <IoIosAddCircle className="inputButton" onClick={addItems}/> </div> </main> ) } export default App;
Items.tsx
import { createContext, ReactNode, useState } from "react"; interface ProviderType{ items: string[], addItem: (name:string) => void removeItem: (name:string) => void editItem: (prevName:string, newName:string) => void } interface ChildrenType{ children: ReactNode } export const ItemsContext = createContext<ProviderType | undefined>(undefined) export const ItemsProvider = ({ children }: ChildrenType) => { const [items,setItems] = useState<string[]>(["Henrique","Iris"]) const addItem = (name: string) => { setItems([...items, name]) } const removeItem = (name: string) => { const newArray = items.filter(item => item !== name) setItems(newArray) } const editItem = (prevName: string, newName: string) => { const newArray = items.map(item => item === prevName ? newName : item) setItems(newArray) } return( <ItemsContext.Provider value={{items, addItem, removeItem, editItem}}> {children} </ItemsContext.Provider> ) }
Card.tsx
import { FaEdit, FaTrash, FaCheck } from "react-icons/fa"; import { useContext, useState, useEffect } from "react"; import { ItemsContext } from "../context/Items"; interface Props{ name: string } function Card(props: Props) { const [toogleEdit, setToogleEdit] = useState<boolean>(false) const [editInput, setEditInput] = useState<string>(props.name) const context = useContext(ItemsContext) // 监听props变化,同步编辑输入框的值 useEffect(() => { setEditInput(props.name) }, [props.name]) const changeEdit = () => { if(!toogleEdit){ setToogleEdit(true) setEditInput(props.name) }else{ setToogleEdit(false) context?.editItem(props.name, editInput) } } return ( <div className="card"> {!toogleEdit && props.name} {toogleEdit && <input type='text' value={editInput} onChange={e => setEditInput(e.target.value)} />} <section className="cardButton"> {!toogleEdit && <FaEdit onClick={changeEdit}/>} {toogleEdit && <FaCheck onClick={changeEdit}/>} <FaTrash onClick={() => context?.removeItem(props.name)}/> </section> </div> ); } export default Card;
内容的提问来源于stack exchange,提问作者Henrique Bandeira
相关产品推荐
相关产品推荐

