skip to Main Content

I’m trying to assing refs dinamically, but I get the error "Invalid hook call. Hooks can only be called inside of the body of a function component" when useRef. Here:

const [subsistemaPlanetario, setSubsistemaPlanetario] = useState([]);
const planetRefs = useRef({});

useEffect(() => {
  async function fetchSubsistemaPlanetario() {
    try {
      const fetchedSubsistemaPlanetario = await getSubsistemaPlanetario();
      setSubsistemaPlanetario(fetchedSubsistemaPlanetario);

      fetchedSubsistemaPlanetario.forEach((planeta) => {
        const camelCaseSlug = planeta.padre.slug.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
        planetRefs.current[camelCaseSlug] = useRef(); // <------THIS LINE DROP AN ERROR
      });
    } catch (error) {
      console.error(error);
    }
  }

  fetchSubsistemaPlanetario();
}, []);

ENTIRE COMPONENT:

import {useFrame} from '@react-three/fiber';
import React, {useRef, useEffect, useState} from 'react';
import {Planet} from './Planet.jsx';
import {Satellite} from './Satellite.jsx';
import {Orbiter} from './utils/Orbiter.js';
import {calculateOrbitalPeriod} from './utils/calculateOrbitalPeriod.js';

import {getSubsistemaPlanetario} from './utils/getSubsistemaPlanetario.js';

export const SubsistemaPlanetario = function SubsistemaPlanetario(props) {
  let running = true;
  let stopRunning = () => (running = false);
  let startRunning = () => (running = true);

  const [subsistemaPlanetario, setSubsistemaPlanetario] = useState([]);
  const planetRefs = useRef({});

  useEffect(() => {
    // Obtener el subsistema planetario cuando el componente se monta
    async function fetchSubsistemaPlanetario() {
      try {
        const fetchedSubsistemaPlanetario = await getSubsistemaPlanetario();
        
        setSubsistemaPlanetario(fetchedSubsistemaPlanetario);

        fetchedSubsistemaPlanetario.forEach((planeta) => {
          const camelCaseSlug = planeta.padre.slug.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());

          planetRefs.current[camelCaseSlug] = useRef();
          console.log(planetRefs);
        });
      } catch (error) {
        console.error(error);
      }
    }

    fetchSubsistemaPlanetario();
  }, []);

  return (
    <>
      {subsistemaPlanetario.map((planetaPadre, index) => (
        <Planet
          key={index}
          scale={0.5}
          ref={planetRefs.current[index]}
          stopRunning={stopRunning}
          startRunning={startRunning}
          textureType="haumea"
          linkTo="areas"
          linkToLabel="Areas"
        />
      ))}
    </>
  );
};

Any help appreciated.

2

Answers


  1. Not sure exactly what you are trying to achieve here.

    ref={planetRefs.current[index]}
    

    will do nothing since planetRefs.current is an empty object and will remain as an empty object since you are not assigning any value to it.

    planetRefs.current[camelCaseSlug] = useRef();
    

    will also do nothing since you can’t use react hooks inside a function that is not a react component.


    By the way – why you are assigning the ref value as camelCaseSlug but in the render you are trying to access it by index?


    Possible solution

    I think you should be using a callback ref instead, to get the access to the rendered elements:

    ref={(ref) => {
       planetRefs.current[index] = ref;
    }}
    

    Note: Remember to wrap your Planet component with forwardRef.

    Edit:

    You can remove the planetRefs.current[camelCaseSlug] = useRef(); part completely. If you prefer to keep the refs in the planetRefs as slugs, just modify the callback ref a bit:

    ref={(ref) => {
       const camelCaseSlug = planetaPadre.padre.slug
          .replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
    
       planetRefs.current[camelCaseSlug] = ref;
    }}
    
    Login or Signup to reply.
  2. You are calling a hook conditionally for your component

    https://legacy.reactjs.org/docs/hooks-rules.html

    Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns. By following this rule, you ensure that Hooks are called in the same order each time a component renders.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search