skip to Main Content

I’m trying to write Reactjs code for scroll to functionality. I want the referenced IDs of different sections be generated for the elements of the ‘lables’ array.

import { useRef } from 'react';

const useDivRefs = (labels) => {
  const divRefs = {};

  labels.forEach((label, i) => {
    const targetId = label.replace(/s+/g, '');
    const ref = useRef(null); 
    divRefs[targetId] = ref;
  });

  return divRefs;
};

const MyComponent = () => {
  const labels = [
    'div 1',
    'div 2',
    'div 3',
  ];

  const divRefs = useDivRefs(labels);

  console.log('divRefs = ', divRefs);

 
  return divRefs; 
};

it works, but the line "const ref = useRef(null); " shows the following error

"React Hook "useRef" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks"

how can I get rid of this error?
Any help would be appreciated.

3

Answers


  1. React hooks are special. You can call React hooks only as top level functions inside React function component or custom hooks.

    Also checkout – https://legacy.reactjs.org/warnings/invalid-hook-call-warning.html

    For your purpose you can use useRef that holds array.

    const divRefs = useRef([]);
    ...
       {items.map((item, i) => (
          <div 
              key={i} 
              ref={el => itemsRef.current[i] = el} >
            ...
          </div>
        ))}
    
    Login or Signup to reply.
  2. React hooks, including "useRef," cannot be called inside any block other than the top-level of a functional component. They must always be called directly within the functional component body.

    Login or Signup to reply.
  3. Due to how React handles hooks, they need to be in a same deterministic order per component instance. That’s why they need to be at the top level of your component, or inside other hooks.

    Therefore, you cannot safely manage an array of refs or an object of refs (there are a lot of other complications with how that would even work if it were possible).

    In similar cases, one could use a ref to an array, or a ref to an object.

    However, your case seems to be a little bit more involved:

    const useDivRefs = (labels) => {
      const divRefs = useRef({});
    
      useEffect(() => {
        const tmp = {};
        labels.forEach((label, i) => {
          const targetId = label.replace(/s+/g, ''); 
          tmp[targetId] = null;
        });
        divRefs.current = tmp;
      }, [labels]);
    
      return divRefs;
    };
    

    Then you can use your ref object as you would have previously, just adding the extra .current.

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