Search code examples
javascriptreactjsthree.jsreact-three-fiberreact-three-drei

R3F: Hooks can only be used within the Canvas component


I am trying to make a Cube with an animated "hello" text on it using threeJs (im using react). It says "Hooks can only be used within the Canvas component!" but I even added the Canvas component in my code. enter image description here

This is the code. What am I doing wrong ? (PS: I am new to React and ThreeJS)

import React, { useRef } from "react";
import { PerspectiveCamera, RenderTexture, Text } from "@react-three/drei";
import { Canvas, useFrame } from "@react-three/fiber";
import styled from "styled-components";

const Container = styled.div`
  height: 100vh;
  width: 100%;
  scroll-snap-align: center;
`

const Test = () => {
  const textRef = useRef();
  useFrame(
    (state) =>
      (textRef.current.position.x = Math.sin(state.clock.elapsedTime) * 2)
  );
  return (
    <Container>
      <Canvas>
        <mesh>
          <boxGeometry />
          <meshStandardMaterial>
            <RenderTexture attach="map">
              <PerspectiveCamera makeDefault position={[0, 0, 5]} />
              <color attach="background" args={["#dc9dcd"]} />
              <Text ref={textRef} fontSize={3} color="#555">
                hello
              </Text>
            </RenderTexture>
          </meshStandardMaterial>
        </mesh>
      </Canvas>
    </Container>
  );
};

export default Test;






Solution

  • As the error says, you are currently calling a react-three-fiber hook not within the Canvas component. You could wrap your text cube in its own component and call useFrame there:

    const TextCube = () => {
    
      const textRef = useRef();
    
      useFrame(
        (state) =>
          (textRef.current.position.x = Math.sin(state.clock.elapsedTime) * 2)
      );
    
      return (
            <mesh>
              <boxGeometry />
              <meshStandardMaterial>
                <RenderTexture attach="map">
                  <PerspectiveCamera makeDefault position={[0, 0, 5]} />
                  <color attach="background" args={["#dc9dcd"]} />
                  <Text ref={textRef} fontSize={3} color="#555">
                    hello
                  </Text>
                </RenderTexture>
              </meshStandardMaterial>
            </mesh>
      );
    };
    

    Then use it in your Test component like this:

    const Test = () => {
      return (
        <Container>
          <Canvas>
            <TextCube />
          </Canvas>
        </Container>
      );
    };
    

    This should to the trick!