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

React Three Fiber中如何更新数组内已有网格的位置?

解决方案

你当前的写法存在两个核心问题:

  1. React元素(比如<MyShape/>)是不可变对象,直接修改props不会触发组件重渲染,也不符合React的状态管理规范。
  2. 直接替换数组中的组件实例虽然能实现效果,但没必要——React本身会复用相同key的组件实例,只要状态数据更新,组件会自动更新自身的position。

正确的做法是将组件的配置数据(而非组件实例本身)存入状态,通过更新数据来驱动组件渲染:

// 只存形状的配置数据,不存组件实例
const [shapeConfigs, setShapeConfigs] = useState([
  { key: 0, position: [0, 0, 0] },
  { key: 1, position: [100, 0, 100] },
  { key: 2, position: [200, 0, 200] }
])

useEffect(() => {
  function handleKeyUp(event) {
    if (event.key === 'r') {
      // 复制原数组,避免直接修改状态
      const updatedConfigs = [...shapeConfigs];
      // 直接更新对应索引的position数据
      updatedConfigs[1].position = [150, 0, 150];
      setShapeConfigs(updatedConfigs);
    }
  }
  window.addEventListener('keyup', handleKeyUp)
  return () => window.removeEventListener('keyup', handleKeyUp)
}, [shapeConfigs])

return (
  <group>
    {/* 根据配置数据渲染组件,key不变React会复用实例 */}
    {shapeConfigs.map(config => (
      <MyShape key={config.key} position={config.position} />
    ))}
  </group>
)

为什么这样可行?

  • 状态中存储的是纯数据,修改数据后通过setShapeConfigs更新状态,会触发React重渲染。
  • 由于每个MyShape的key保持不变,React会复用已有的组件实例,只更新其position属性,完全符合你「不创建新实例,只更新已有形状位置」的需求。

如果坚持要直接操作组件实例(不推荐,违反React声明式编程原则),可以通过useRef存储组件的实例引用,但这种方式需要MyShape暴露修改position的方法,示例如下:

const shapeRefs = useRef([]);

const [shapes, setShapes] = useState([
  <MyShape ref={el => shapeRefs.current[0] = el} key={0} position={[0, 0, 0]} />,
  <MyShape ref={el => shapeRefs.current[1] = el} key={1} position={[100, 0, 100]} />,
  <MyShape ref={el => shapeRefs.current[2] = el} key={2} position={[200, 0, 200]} />
])

useEffect(() => {
  function handleKeyUp(event) {
    if (event.key === 'r') {
      // 调用MyShape内部暴露的更新位置方法(需要MyShape支持)
      shapeRefs.current[1].updatePosition([150, 0, 150]);
      // 不需要调用setShapes,因为直接操作实例状态
    }
  }
  window.addEventListener('keyup', handleKeyUp)
  return () => window.removeEventListener('keyup', handleKeyUp)
}, [])

// MyShape组件需要实现updatePosition方法,比如:
const MyShape = forwardRef((props, ref) => {
  const [position, setPosition] = useState(props.position);
  
  // 暴露更新方法给父组件
  useImperativeHandle(ref, () => ({
    updatePosition(newPos) {
      setPosition(newPos);
    }
  }));
  
  return <roundedBox position={position} />;
});

但这种命令式的写法会让代码复杂度上升,也不符合React的设计理念,优先推荐第一种基于数据驱动的方案。

内容的提问来源于stack exchange,提问作者jenepix

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 11:50:46