React+Three.js集成3D模型遇R3F钩子报错:仅能在Canvas内使用
解决R3F: Hooks can only be used within the Canvas component! 错误
问题重现
使用React + Three.js(react-three-fiber)渲染3D模型时,收到错误提示:
R3F: Hooks can only be used within the Canvas component!
已将<Model1 />置于<Canvas>标签内,但网站仍无法加载,核心代码如下:
Home.js 核心代码
<Canvas className={`w-full h-screen bg-transparent ${isRotating ? 'cursor-grabbing' : 'cursor-grab'}`} camera={{ near: 0.1, far: 1000, position: [0, 0, 10], fov: 10 }} > <ambientLight intensity={0.4} /> <pointLight position={[10, 5, 10]} intensity={2} /> <directionalLight position={[1, 1, 1]} intensity={2} /> <spotLight position={[10, 5, 10]} angle={0.15} penumbra={1} intensity={2} /> <Suspense fallback={loader}> <Model1 position={modelPosition} rotation={[0.1, 4.7077, 0]} scale={modelScale} isRotating={isRotating} setIsRotating={setIsRotating} /> </Suspense> </Canvas> // 其他相关代码 useEffect(() => { adjustModelForScreenSize(); const handleResize = () => adjustModelForScreenSize(); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); const loader = <div style={{ textAlign: 'center' }}>Loading...</div>;
Model1.js 代码
import React, { useRef } from "react"; import { useGLTF } from "@react-three/drei"; import { useFrame } from "@react-three/fiber"; const Model1 = ({ position, rotation, scale, isRotating, setIsRotating, ...props }) => { const modelRef = useRef(); const { scene } = useGLTF("/character_mannequin_male.glb"); useFrame(() => { if (isRotating && modelRef.current) { modelRef.current.rotation.y += 0.01; } }); return ( <primitive object={scene} ref={modelRef} position={position} rotation={rotation} scale={scale} {...props} /> ); }; useGLTF.preload("/character_mannequin_male.glb"); // 错误根源 export default Model1;
错误原因
useGLTF.preload()属于react-three-fiber生态的方法,必须在Canvas组件的上下文环境内执行。你把它写在了Model1组件的外部,脱离了Canvas的上下文,触发了R3F的钩子使用限制。
解决方案
将useGLTF.preload()移到Model1组件内部,确保它处于Canvas的上下文范围内。
修改后的Model1.js
import React, { useRef } from "react"; import { useGLTF } from "@react-three/drei"; import { useFrame } from "@react-three/fiber"; const Model1 = ({ position, rotation, scale, isRotating, setIsRotating, ...props }) => { const modelRef = useRef(); // 将预加载调用移到组件内部 useGLTF.preload("/character_mannequin_male.glb"); const { scene } = useGLTF("/character_mannequin_male.glb"); useFrame(() => { if (isRotating && modelRef.current) { modelRef.current.rotation.y += 0.01; } }); return ( <primitive object={scene} ref={modelRef} position={position} rotation={rotation} scale={scale} {...props} /> ); }; export default Model1;
补充:实现鼠标拖拽旋转功能
如果你需要鼠标拖拽旋转模型,推荐使用@react-three/drei提供的OrbitControls,它能快速实现拖拽、缩放等交互,无需手动维护isRotating状态:
在Home.js的Canvas内添加OrbitControls:
import { OrbitControls } from '@react-three/drei'; <Canvas ...> {/* 其他灯光组件 */} <OrbitControls enableZoom={false} // 按需关闭缩放 enablePan={false} // 按需关闭平移 /> <Suspense fallback={loader}> <Model1 ... /> </Suspense> </Canvas>
添加后用户即可直接拖拽模型旋转,同时可以移除自己手动维护的isRotating相关逻辑,简化代码。
内容的提问来源于stack exchange,提问作者Deno
相关产品推荐
相关产品推荐

