React中如何正确使用setState修改state内的对象?
解决React setState修改嵌套对象时丢失数据的问题
问题现象
调用handleClick修改buttons数组中对象的isHeld属性后,state结构被破坏——原本包含myQuestions和buttons的对象数组,变成了仅包含buttons数组的数组,导致后续渲染时出现Cannot read properties of undefined (reading 'map')错误。
错误原因
你的handleClick函数中嵌套map的逻辑错误:外层prevState.map直接返回了item.buttons.map的结果,没有保留原对象的myQuestions属性,直接把每个问题对象替换成了它的buttons数组,最终整个state变成二维数组,完全丢失原有结构。
修复方案
修改setState的更新逻辑,保留原问题对象的所有属性,仅更新目标按钮的isHeld状态:
- 遍历每个问题对象时,先展开原对象(保留
myQuestions等属性) - 单独对
buttons数组进行遍历,修改匹配id的按钮的isHeld值
修正后的handleClick代码:
function handleClick(e) { setState(prevState => prevState.map(questionItem => ({ // 保留原问题对象的所有属性 ...questionItem, // 仅更新buttons数组中匹配id的项 buttons: questionItem.buttons.map(button => button.id === e ? {...button, isHeld: !button.isHeld} : button ) }))) }
额外优化点
渲染Quiz组件时,item.buttons.map(item => item)属于冗余操作,直接传递item.buttons即可:
const getGame = state.map(item => <Quiz buttons={item.buttons} handleClick={(e) => handleClick(e.target.name)} questions={item.myQuestions} /> )
完整修正后代码示例
import React from 'react'; import { v4 as uuidv4 } from 'uuid'; // 假设Quiz组件已定义 function Quiz({ buttons, handleClick, questions }) { return ( <div> <h3>{questions}</h3> {buttons.map(btn => ( <button key={btn.id} name={btn.id} onClick={handleClick} style={{ backgroundColor: btn.isHeld ? 'lightblue' : 'white' }} > {btn.use} </button> ))} </div> ); } function App() { const [state, setState] = React.useState([]); React.useEffect(()=> { fetch("https://opentdb.com/api.php?amount=5&category=27&difficulty=easy&type=multiple") .then(res => res.json()) .then((data) => setState(data.results.map(item => ({ myQuestions: item.question, buttons: item.incorrect_answers.concat(item.correct_answer).map(item => ({use:item, isHeld:false, id:uuidv4()})) })))) }, []) const getGame = state.map((item, index) => <Quiz key={index} buttons={item.buttons} handleClick={(e) => handleClick(e.target.name)} questions={item.myQuestions} /> ) function handleClick(e) { setState(prevState => prevState.map(questionItem => ({ ...questionItem, buttons: questionItem.buttons.map(button => button.id === e ? {...button, isHeld: !button.isHeld} : button ) }))) } return ( <div className="App"> {getGame} </div> ); } export default App;
内容的提问来源于stack exchange,提问作者CRISVOSS
相关产品推荐
相关产品推荐

