Search code examples
javascriptreactjs3djsxthreejs-editor

I am getting" Invalid hook call. Hooks can only be called inside of the body of a function component. when i am trying to add an object to a scene"


I am using threeJS in react and i am trying to add an object into my scene to have some specific functions that update regularly.

Here is my code for the object:

import React, { useRef, useEffect } from "react";
import { useFrame, useThree } from "react-three-fiber";
import * as THREE from "three";

const Actor = () => {
  const meshRef = useRef();
  const { scene } = useThree();

  useEffect(() => {
    const geometry = new THREE.BoxGeometry(1, 1, 1);
    const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
    const mesh = new THREE.Mesh(geometry, material);
    mesh.rotateY(Math.PI);

    // Set initial properties and functions
    mesh.instructions = [];
    mesh.target = new THREE.Object3D().copy(mesh, false);
    mesh.targetRadiansOnY = 0;
    mesh.currentRadiansOnY = 0;
    mesh.mass = 0.1;
    mesh.velocity = new THREE.Vector3();
    mesh.angularVelocity = 0.015;
    mesh.topSpeed = 0.05;
    mesh.topAccelleration = 0.0015;
    mesh.accelleration = new THREE.Vector3();
    mesh.currentInstruction = null;
    mesh.gravityForce = new THREE.Vector3(0.0, -0.01, 0.0);

    // Add mesh to the scene
    scene.add(mesh);
    meshRef.current = mesh;

    return () => {
      // Clean up the mesh when the component unmounts
      scene.remove(mesh);
    };
  }, [scene]);

  useEffect(() => {
    // Update function to consume commands
    const consumeCommands = () => {
      const mesh = meshRef.current;
      if (mesh.currentInstruction) {
        let instruction = mesh.currentInstruction;
        let movementType = instruction["type"];
        let movementValue = instruction["value"];
        let dir = null;
        switch (movementType) {
          case "move_forward":
            dir = new THREE.Vector3().subVectors(mesh.target.position, mesh.position);
            dir.setLength(mesh.topAccelleration);
            mesh.applyForce(dir);
            mesh.applyForce(mesh.gravityForce);
            mesh.velocity.add(mesh.accelleration);
            mesh._limitVelocity(mesh.topSpeed);
            mesh.position.add(mesh.velocity);
            mesh._limitGravity();
            mesh.accelleration.multiplyScalar(0.0);
            break;
          case "jump_forward":
            dir = new THREE.Vector3().subVectors(mesh.target.position, mesh.position);
            let upForce = new THREE.Vector3(0, (0.012 * (dir.length() / movementValue)), 0);
            dir.setLength(mesh.topAccelleration);
            mesh.applyForce(dir);
            mesh.applyForce(upForce);
            mesh.applyForce(mesh.gravityForce);
            mesh.velocity.add(mesh.accelleration);
            mesh._limitVelocity(mesh.topSpeed);
            mesh.position.add(mesh.velocity);
            mesh._limitGravity();
            mesh.accelleration.multiplyScalar(0.0);
            break;
          case "turn":
            if (movementValue === "turnLeft") {
              mesh.rotateY(mesh.angularVelocity);
              mesh.currentRadiansOnY += mesh.angularVelocity;
            } else {
              mesh.rotateY(-mesh.angularVelocity);
              mesh.currentRadiansOnY += mesh.angularVelocity;
            }
            break;
          default:
            console.log("command not implemented");
            break;
        }
        if (mesh._targetReached(movementType)) {
          mesh._nextAnimation();
        }
      }
    };

    // Set the consumeCommands function as an update function
    useFrame(() => {
      consumeCommands();
    });
  }, []);

  return null; // We don't render anything for this component
};

export default Actor;

When i run this code, I get the error Invalid hook call. Hooks can only be called inside of the body of a function component. I don't know what I am doing wrong. This is my first time using threeJS in react.


Solution

  • You can't call hooks inside hooks. Which you do in your case. useFrame hook called inside your useEffect

    You don't need to worry about the mount and unmount states of useFrame which is already handled by the library.

    This hook allows you to execute code on every rendered frame, like running effects, updating controls, and so on. You receive the state (same as useThree) and a clock delta. Your callback function will be invoked just before a frame is rendered. When the component unmounts it is unsubscribed automatically from the render-loop.

    You just need to remove your second useEffect and move everything inside that to out. Directly in the component.