React Three Fiber中如何更新数组内已有网格的位置?
解决方案
你当前的写法存在两个核心问题:
- React元素(比如
<MyShape/>)是不可变对象,直接修改props不会触发组件重渲染,也不符合React的状态管理规范。 - 直接替换数组中的组件实例虽然能实现效果,但没必要——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
相关产品推荐
相关产品推荐

