2

Am creating a panel using react-modal, everything seems to be working fine.

But once the modal is opened, I need to set the top value of my modal. I am using hooks, and whenever am trying to update the value, getting into error, hooks can be called only inside the React functions. I tried all the possible ways but not able to solve the issue.

Expectation: - OnAfterOpen should be called, and the latest state value should be passed to pos prop.

This is what I have tried.

function useTop() {
  const [top, setTop] = useState('100px');

  useEffect(() => {
    const handleResize = () => {
      const hasRefClientHeight = contentRef.current && contentRef.current.clientHeight;
      hasRefClientHeight < window.innerHeight ? setTop(`calc(100% - ${hasRefClientHeight}px)`) : setTop('100px');
    };
    window.addEventListener('resize', handleResize);
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []);

  return top;
}

const Panel = () => {
   // const newTop = useTop(); -- This is not working as modal content is not ready. Modal Content will be ready only if `onAfterOpen` triggers.

   <Modal pos={useTop} onAfterOpen={useTop}><div ref={contentRef}>Test</div></Modal>
};

2 Answers 2

2

Indeed, the rules of hooks says:

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

Only Call Hooks at the Top Level

Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple useState and useEffect calls. (If you’re curious, we’ll explain this in depth below.)

This is necessary for React to be sure that hooks will be called on exact the same order in every render cycle.

It seems that Modal would call useTop only after mounted (opened), i.e. it will probably have skipped the first render, violating the rule of hooks and messing with the order.

One thing you can try:

Pass the ref for your useTop hook and handle it inside of it. If it's null, don't do anything, if it's already mounted, attach the listener and return the cleanup function.

const Panel = () => {
  const newTop = useTop(contentRef);
  <Modal pos={newTop} onAfterOpen={null}><div ref={contentRef}>Test</div></Modal>
};
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you, wont it impact performance if I call same function twice for example const Panel = () => { const newTop = useTop(contentRef); <Modal pos={newTop} onAfterOpen={useTop}><div ref={contentRef}>Test</div></Modal> }; Now useTop function is called twice one in AfterOpen and other at the top of the component. Is this a better way?
The impact would be neglible. But I don't think you'll be able to execute it as a hook on the onAfterOpen property. I think you will be getting the same error. Sorry if my answer was leading you that way. I've updated it. Your useTop hook will need to be called only inside the body of your component function.
Thank you let me give a try
0

Along with newTop you can return a function that triggers recalculation of the newTop. Similar to how useState does. And invoke it onAfterOpen.

Comments

Your Answer

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