React.js中onChange获取值报错及Axis数组删除异常问题求助
嘿,我来帮你搞定这两个React状态更新的问题,都是因为状态更新时没有遵循不可变更新原则导致的,咱们一步步来修复:
问题1:输入文本时出现“Axis.map is not a function”错误
原因分析
在AppendingAxis组件的输入框onChange事件里,你写了这样的代码:
onChange={e => setAxis((Axis[index].label = e.target.value))}
这里Axis[index].label = e.target.value是一个赋值操作,执行后返回的是输入框的字符串值。调用setAxis后,原本是数组类型的Axis状态就被替换成了字符串,后续渲染时Axis.map()自然会报错——因为字符串根本没有map方法。
修复方案
更新状态时必须创建原数组的副本,修改对应项后再设置新状态,同时用函数式更新确保拿到最新的状态值:
// AppendingAxis组件的onChange修改为: onChange={e => { setAxis(prevAxis => { // 先复制原数组,避免直接修改状态 const updatedAxis = [...prevAxis]; // 复制对应索引的对象,修改label属性(同样避免直接修改原对象) updatedAxis[index] = { ...updatedAxis[index], label: e.target.value }; return updatedAxis; }); }}
问题2:点击删除按钮后除第一项外的Axis数据全部消失
原因分析
你的deleteAxis方法里用了splice:
const deleteAxis = () => { setAxis(Axis.splice(-1, 1)); };
splice是一个会直接修改原数组的方法,并且它的返回值是被删除的元素组成的数组。比如原Axis是[objA, objB, objC],执行Axis.splice(-1,1)后,原数组变成[objA, objB],但返回值是[objC]——你把这个返回值传给setAxis,导致Axis状态直接变成了[objC],看起来就像之前的项都消失了。
修复方案
改用不会修改原数组的方式创建新数组,比如用slice,或者先复制原数组再执行splice:
// SetElements组件的deleteAxis修改为: const deleteAxis = () => { // 方式1:用slice返回从开头到倒数第二项的新数组(推荐,更简洁) setAxis(prevAxis => prevAxis.slice(0, -1)); // 方式2:先复制数组再删除,适合更复杂的删除场景 // setAxis(prevAxis => { // const newAxis = [...prevAxis]; // newAxis.splice(-1, 1); // return newAxis; // }); };
另外,原代码里删除按钮的显示条件Axis.length !== 1不太准确,建议改成Axis.length > 1,这样当数组只有一项时就不会显示删除按钮了,避免误删最后一项。
最后附上修改后的完整代码片段
SetElements组件
const SetElements = ({ ... }) => { const [Axis, setAxis] = useState([]); const AxisHandler = e => { setAxis([ ...Axis, { label: "", data: "", backgroundColor: "" }, ]); }; const deleteAxis = () => { setAxis(prevAxis => prevAxis.slice(0, -1)); }; return ( <> <button onClick={AxisHandler}>add Line</button> {Axis.length > 1 && ( <button onClick={deleteAxis}>delete Line</button> )} {Axis.map((element, index) => ( <AppendingAxis key={index} {/* 别忘了加key,避免React渲染警告 */} Axis={Axis} setAxis={setAxis} element={element} index={index} /> ))} </> ) }
AppendingAxis组件
const AppendingAxis = ({ index, setAxis, Axis }) => { console.log(Axis); return ( <AxisSetting> <h4>{index + 2}Y Axis setting</h4> <span> <input placeholder={`${index + 2} setting`} type="text" {/* 这里应该是text,textarea是单独的标签 */} onChange={e => { setAxis(prevAxis => { const updatedAxis = [...prevAxis]; updatedAxis[index] = { ...updatedAxis[index], label: e.target.value }; return updatedAxis; }); }} /> </span> </AxisSetting> ) }
(注:顺便帮你修正了输入框的type属性,textarea是独立的HTML标签,输入框应该用type="text")
内容的提问来源于stack exchange,提问作者Somi

