React受控输入切换选中元素后值不更新问题求助
问题
制作简易HTML编辑工具时,切换选中元素后,侧边面板的输入框值不会更新为新选中元素的属性值。目标对象的值已正常变更,但切换对象时输入框保留之前的内容,仅第一次选择元素时能正确显示字段值。
代码示例:
import "./styles.css"; import { useState } from "react"; const exampleThings = [ { x: "12px", i: 0, a: "foo", }, { x: "5px", i: 1, a: "bar", }, ]; const positionParse = (pos) => { const parsed = parseFloat(pos); if (isNaN(parsed)) throw new Error("oops"); return parsed; }; function PositionEditor({ setPositionDef, positionDef }) { const [hasError, setHasError] = useState(false); const [value, setValue] = useState(positionDef); const changed = (e) => { setValue(e.target.value); try { positionDef = positionParse(e.target.value); setHasError(false); setPositionDef(e.target.value); } catch (e) { setHasError(true); } }; return ( <input type="text" value={value} onChange={changed} style={{ borderColor: hasError ? "red" : "unset" }} /> ); } function ThingSelector({ select, t }) { return <button onClick={select}>Select thing {t} </button>; } export default function App() { const [things, setThings] = useState([...exampleThings]); const [selectedThing, setSelectedThing] = useState(undefined); const selectThing = (i) => { const thingIndex = things.findIndex((thing) => thing.i === i); console.log("select thing", thingIndex); setSelectedThing(things[thingIndex]); }; const editThingPositionDef = (position) => { const selectedThingId = things.findIndex((t) => t.i === selectedThing.i); const newThings = [...things]; const newSelectedThing = { ...selectedThing, x: position, }; newThings[selectedThingId] = { position, ...newSelectedThing, }; setThings(newThings); setSelectedThing(newSelectedThing); }; return ( <div className="App"> {things.map((thing) => { return ( <ThingSelector select={(e) => selectThing(thing.i)} t={thing.a} key={thing.i} /> ); })} {selectedThing === undefined ? ( <p>Select something</p> ) : ( <> <p> Editing: {selectedThing.a}. My x is {positionParse(selectedThing.x)} </p> <PositionEditor setPositionDef={editThingPositionDef} positionDef={selectedThing.x} /> </> )} </div> ); }
解决方案
问题核心:PositionEditor组件内部维护了独立的value状态,仅在组件初始化时从positionDef props赋值一次。当切换选中元素时,positionDef props更新,但组件内部的value状态未同步,导致输入框显示旧值。
方法一:移除内部状态,直接依赖props(推荐)
组件不需要单独维护输入值状态,直接绑定父组件传入的positionDef作为输入框值,变更时同步到父组件状态即可。
修改后的PositionEditor组件:
function PositionEditor({ setPositionDef, positionDef }) { const [hasError, setHasError] = useState(false); const changed = (e) => { const inputValue = e.target.value; try { positionParse(inputValue); setHasError(false); setPositionDef(inputValue); } catch (e) { setHasError(true); } }; return ( <input type="text" value={positionDef} onChange={changed} style={{ borderColor: hasError ? "red" : "unset" }} /> ); }
方法二:用useEffect同步props到内部状态
如果需要保留组件内部临时状态(比如处理输入验证时的未提交值),可以通过useEffect监听positionDef的变化,同步更新内部value:
import { useState, useEffect } from "react"; function PositionEditor({ setPositionDef, positionDef }) { const [hasError, setHasError] = useState(false); const [value, setValue] = useState(positionDef); // 监听props变化,同步到内部状态 useEffect(() => { setValue(positionDef); setHasError(false); // 切换元素时重置错误状态 }, [positionDef]); const changed = (e) => { setValue(e.target.value); try { positionParse(e.target.value); setHasError(false); setPositionDef(e.target.value); } catch (e) { setHasError(true); } }; return ( <input type="text" value={value} onChange={changed} style={{ borderColor: hasError ? "red" : "unset" }} /> ); }
额外优化
父组件editThingPositionDef函数中存在冗余赋值,可简化:
const editThingPositionDef = (position) => { const selectedThingId = things.findIndex((t) => t.i === selectedThing.i); const newThings = [...things]; const newSelectedThing = { ...selectedThing, x: position, }; // 移除冗余的position属性 newThings[selectedThingId] = newSelectedThing; setThings(newThings); setSelectedThing(newSelectedThing); };
内容的提问来源于stack exchange,提问作者Joaquin
相关产品推荐
相关产品推荐

