react-three-fiber useTexture报仅能在Canvas内使用错误排查
问题说明
报错信息
- 报错内容:
Uncaught R3F hooks can only be used within the Canvas component! - 报错触发位置:
const terrainTexture = useTexutre(url);
报错对应代码:
import { OrbitControls, Sky, useTexture } from '@react-three/drei'; import { Canvas } from '@react-three/fiber'; ... function App() { const terrainTexture = useTexture('/Environments/Tile/Tile_2048.png'); return ( <Canvas> ... <mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, -2, 0]}> <planeBufferGeometry args={[100,100]}/> <meshStandardMaterial map={terrainTexture}/> </mesh> ... </Canvas> ); } export default App;
参考正常运行代码
import { useGLTF, useAnimations, useTexture } from '@react-three/drei' ... export default function ModelAnim({ ...props }) { ... const bakedTexture = useTexture('/Characters/Textures/Character_Texture_1024.png') useEffect(() => { ... }, []) return ( <group ref={group} {...props} dispose={null}> ... <skinnedMesh geometry={nodes.Character.geometry} material={materials.Mat_Character} skeleton={nodes.Character.skeleton} > <meshStandardMaterial map={bakedTexture} map-flipY={false}/> </skinnedMesh> </group> ) } useGLTF.preload('/Character.gltf')
核心差异解答
两段代码的核心区别是R3F(React Three Fiber)相关hook的调用位置是否在Canvas组件提供的上下文范围内:
@react-three/fiber和@react-three/drei提供的所有hook(包括useTexture、useGLTF、useFrame等),都依赖Canvas组件注入的React Context,只有Canvas的后代组件才能访问到对应的上下文资源,直接在渲染Canvas的父组件里调用这些hook,会因为拿不到上下文直接抛出题目中的错误。- 报错代码里,
useTexture写在App组件中,而App组件本身是渲染<Canvas>的父组件,调用hook时还处于Canvas上下文外部,不符合调用规则,因此报错。 - 可正常运行的代码里,
useTexture写在ModelAnim组件内部,而ModelAnim是作为子组件被放在<Canvas>标签内渲染的,调用hook时已经处于Canvas的上下文覆盖范围内,符合R3F的hook调用要求,因此可以正常运行。
修复方案
把调用R3F hook、渲染3D对象的逻辑抽离成独立子组件,放到Canvas内部渲染即可,修复示例:
import { OrbitControls, Sky, useTexture } from '@react-three/drei'; import { Canvas } from '@react-three/fiber'; // 抽离Canvas内部的场景子组件 function Terrain() { // 在Canvas子组件内调用hook,符合规则 const terrainTexture = useTexture('/Environments/Tile/Tile_2048.png'); return ( <mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, -2, 0]}> <planeBufferGeometry args={[100,100]}/> <meshStandardMaterial map={terrainTexture}/> </mesh> ) } function App() { return ( <Canvas> <OrbitControls /> <Sky /> <Terrain /> </Canvas> ); } export default App;
内容的提问来源于stack exchange,提问作者Ban
相关产品推荐
相关产品推荐

