React组件按钮点击视图不更新+右键点击无响应求助
React组件AssemblyLine问题解决方案
问题1:点击按钮后状态更新但视图未实时刷新
原因
你在addNewButton和moveButton函数中直接修改了buttonsColumns原状态对象:
let updatedObject = buttonsColumns updatedObject[props.stages[0]].unshift(newInput)
React的useState依赖引用变化触发重渲染,直接修改原对象不会改变状态引用,React会判定状态未更新,因此不会重新渲染组件。只有当newInput状态更新时,才会连带触发重渲染,显示之前的状态变更。
解决办法
每次更新状态时创建新的对象/数组副本,确保引用发生变化:
- 修改
addNewButton函数:
function addNewButton () { // 复制原状态对象,创建新引用 let updatedObject = { ...buttonsColumns } // 对目标数组创建副本并添加新元素 updatedObject[props.stages[0]] = [newInput, ...updatedObject[props.stages[0]]] setButtonsColumns(updatedObject) setNewInput("") }
- 修改
moveButton函数中的状态更新逻辑:
function moveButton (e, stage, input) { // 创建状态对象副本 let updatedObject = { ...buttonsColumns } // 过滤数组时返回新数组 updatedObject[stage] = updatedObject[stage].filter(x => x !== input) if (e.button === 0) { console.log("左键点击") let positionForward = props.stages.indexOf(stage) + 1 if (positionForward < props.stages.length) { // 创建新数组添加元素 updatedObject[props.stages[positionForward]] = [input, ...updatedObject[props.stages[positionForward]]] } } if (e.button === 2) { console.log("右键点击") let positionBackward = props.stages.indexOf(stage) - 1 // 修正边界判断,允许回退到第一个阶段 if (positionBackward >= 0) { updatedObject[props.stages[positionBackward]] = [...updatedObject[props.stages[positionBackward]], input] } } setButtonsColumns(updatedObject) }
- 优化状态初始化,避免每次渲染重复创建对象:
const [buttonsColumns, setButtonsColumns] = useState(() => { let columns = {} for (let stage of props.stages) { columns[stage] = [] } return columns })
问题2:无法捕获右键点击
原因
onClick事件仅能捕获左键点击(鼠标按钮0),右键点击对应的是onContextMenu事件,且浏览器默认会弹出右键菜单,会覆盖自定义逻辑。
解决办法
使用onMouseDown事件捕获所有鼠标按键操作,同时阻止右键默认菜单弹出:
修改按钮的事件绑定:
<button data-testid="assembly-item" key={input} onMouseDown={(e) => moveButton(e, stage, input)} // 阻止右键默认菜单 onContextMenu={(e) => e.preventDefault()} > {input} </button>
也可以直接在moveButton函数中阻止默认行为:
function moveButton (e, stage, input) { if (e.button === 2) { e.preventDefault() } // 剩余逻辑... }
修改后的完整组件代码
const AssemblyLine = props => { const [buttonsColumns, setButtonsColumns] = useState(() => { let columns = {} for (let stage of props.stages) { columns[stage] = [] } return columns }) const [newInput, setNewInput] = useState("") const buttons = (stage) => { return buttonsColumns[stage].map((input) => ( <button data-testid="assembly-item" key={input} onMouseDown={(e) => moveButton(e, stage, input)} onContextMenu={(e) => e.preventDefault()} > {input} </button> )) } const stages = props.stages.map((stage, index) => ( <div data-testid="stage" key={index} > <span>{stage}</span> {buttons(stage)} </div> )) function addNewButton () { let updatedObject = { ...buttonsColumns } updatedObject[props.stages[0]] = [newInput, ...updatedObject[props.stages[0]]] setButtonsColumns(updatedObject) setNewInput("") } function moveButton (e, stage, input) { if (e.button === 2) { e.preventDefault() } let updatedObject = { ...buttonsColumns } updatedObject[stage] = updatedObject[stage].filter(x => x !== input) if (e.button === 0) { console.log("左键点击") let positionForward = props.stages.indexOf(stage) + 1 if (positionForward < props.stages.length) { updatedObject[props.stages[positionForward]] = [input, ...updatedObject[props.stages[positionForward]]] } } if (e.button === 2) { console.log("右键点击") let positionBackward = props.stages.indexOf(stage) - 1 if (positionBackward >= 0) { updatedObject[props.stages[positionBackward]] = [...updatedObject[props.stages[positionBackward]], input] } } setButtonsColumns(updatedObject) } return ( <> <input name="newButton" data-testid="add-item" onKeyDown={e => e.key === "Enter" ? addNewButton() : null} onChange={e => setNewInput(e.target.value)} value={newInput} /> <div id="stagesContainer"> {stages} </div> </> ); }; export default AssemblyLine;
内容的提问来源于stack exchange,提问作者Nazim M.
相关产品推荐
相关产品推荐

