0

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.

0

4 Answers 4

4

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>
    ))}
Sign up to request clarification or add additional context in comments.

Comments

1

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.

1 Comment

I find this answer is the closest, thanks so much.
0

It's possible to create refs programmatically as such:

const [refMapping, setRefMapping] = useState< { [key: string]: any }>({});

useEffect(() => {
  // Create a new array of refs whenever items change
  const refMapping = myList.reduce((prevVal, curVal) => {
    const newObject: any = { ...prevVal }
    newObject[curVal.key] = React.createRef()
    return newObject
  }, {})
  setRefMapping(refMapping);
}, [myList]);

Comments

-1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.