You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React组件按钮点击视图不更新+右键点击无响应求助

React组件AssemblyLine问题解决方案

问题1:点击按钮后状态更新但视图未实时刷新

原因

你在addNewButton和moveButton函数中直接修改了buttonsColumns原状态对象:

let updatedObject = buttonsColumns
updatedObject[props.stages[0]].unshift(newInput)

React的useState依赖引用变化触发重渲染,直接修改原对象不会改变状态引用,React会判定状态未更新,因此不会重新渲染组件。只有当newInput状态更新时,才会连带触发重渲染,显示之前的状态变更。

解决办法

每次更新状态时创建新的对象/数组副本,确保引用发生变化:

  1. 修改addNewButton函数:
function addNewButton () {
  // 复制原状态对象,创建新引用
  let updatedObject = { ...buttonsColumns }
  // 对目标数组创建副本并添加新元素
  updatedObject[props.stages[0]] = [newInput, ...updatedObject[props.stages[0]]]
  setButtonsColumns(updatedObject)
  setNewInput("")
}
  1. 修改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)
}
  1. 优化状态初始化,避免每次渲染重复创建对象:
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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 15:47:14