Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalid hook call when using it inside a library #256

Closed
leticiafontoura opened this issue Jul 25, 2023 · 3 comments
Closed

Invalid hook call when using it inside a library #256

leticiafontoura opened this issue Jul 25, 2023 · 3 comments

Comments

@leticiafontoura
Copy link

leticiafontoura commented Jul 25, 2023

Hello!

The project I work on has its own npm package with our design system's components.

We use FocusLock on two different components but for some reason, when we import these components into the project, we get this error on screen:

image

the error only occurs when we use npm link <lib_name> to test the lib local

@theKashey
Copy link
Owner

Sorry, image with generic stack trace is not debuggable.

@leticiafontoura
Copy link
Author

I understand, not sure this will be helpful but here is the full log from console:

react.development.js:1279 Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.
    at resolveDispatcher (react.development.js:1279:1)
    at useEffect (react.development.js:1317:1)
    at x (index.js:142:1)
    at renderWithHooks (react-dom.development.js:12871:1)
    at mountIndeterminateComponent (react-dom.development.js:15201:1)
    at beginWork (react-dom.development.js:16138:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js:168:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:212:1)
    at invokeGuardedCallback (react-dom.development.js:260:1)
    at beginWork$1 (react-dom.development.js:20041:1)
resolveDispatcher @ react.development.js:1279
useEffect @ react.development.js:1317
x @ index.js:142
renderWithHooks @ react-dom.development.js:12871
mountIndeterminateComponent @ react-dom.development.js:15201
beginWork @ react-dom.development.js:16138
callCallback @ react-dom.development.js:168
invokeGuardedCallbackDev @ react-dom.development.js:212
invokeGuardedCallback @ react-dom.development.js:260
beginWork$1 @ react-dom.development.js:20041
performUnitOfWork @ react-dom.development.js:19162
workLoopSync @ react-dom.development.js:19141
performSyncWorkOnRoot @ react-dom.development.js:18811
(anonymous) @ react-dom.development.js:9782
unstable_runWithPriority @ scheduler.development.js:571
runWithPriority$1 @ react-dom.development.js:9737
flushSyncCallbackQueueImpl @ react-dom.development.js:9778
flushSyncCallbackQueue @ react-dom.development.js:9768
flushPassiveEffectsImpl @ react-dom.development.js:19774
unstable_runWithPriority @ scheduler.development.js:571
runWithPriority$1 @ react-dom.development.js:9737
flushPassiveEffects @ react-dom.development.js:19724
(anonymous) @ react-dom.development.js:19621
workLoop @ scheduler.development.js:526
flushWork @ scheduler.development.js:488
performWorkUntilDeadline @ scheduler.development.js:141
react-dom.development.js:16918 The above error occurred in the <x> component:
    in x (at ModalWarningIncompleteData.tsx:55)
    in ModalWarningPrenatal (at BottomBarAttendance.tsx:410)
    in div (at BottomBarAttendance.tsx:336)
    in BottomBarAttendance (created by Connect(BottomBarAttendance))
    in Connect(BottomBarAttendance) (at MedicalRecord.tsx:448)
    in div (at MedicalRecord.tsx:388)
    in MedicalRecord (created by Connect(MedicalRecord))
    in Connect(MedicalRecord) (created by Context.Consumer)
    in Route (at ProtectRoutes.js:15)
    in Protect (at routes/index.tsx:67)
    in Routes (at App.js:40)
    in UserStorage (at App.js:38)
    in Router (created by BrowserRouter)
    in BrowserRouter (at App.js:37)
    in Provider (at App.js:36)
    in div (at App.js:35)
    in App (at src/index.js:33)

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://fb.me/react-error-boundaries to learn more about error boundaries.
overrideMethod @ console.js:213
logCapturedError @ react-dom.development.js:16918
logError @ react-dom.development.js:16949
update.callback @ react-dom.development.js:17899
callCallback @ react-dom.development.js:10958
commitUpdateQueue @ react-dom.development.js:10976
commitLifeCycles @ react-dom.development.js:17217
commitLayoutEffects @ react-dom.development.js:19710
callCallback @ react-dom.development.js:168
invokeGuardedCallbackDev @ react-dom.development.js:212
invokeGuardedCallback @ react-dom.development.js:260
commitRootImpl @ react-dom.development.js:19487
unstable_runWithPriority @ scheduler.development.js:571
runWithPriority$1 @ react-dom.development.js:9737
commitRoot @ react-dom.development.js:19351
finishSyncRender @ react-dom.development.js:18855
performSyncWorkOnRoot @ react-dom.development.js:18844
(anonymous) @ react-dom.development.js:9782
unstable_runWithPriority @ scheduler.development.js:571
runWithPriority$1 @ react-dom.development.js:9737
flushSyncCallbackQueueImpl @ react-dom.development.js:9778
flushSyncCallbackQueue @ react-dom.development.js:9768
flushPassiveEffectsImpl @ react-dom.development.js:19774
unstable_runWithPriority @ scheduler.development.js:571
runWithPriority$1 @ react-dom.development.js:9737
flushPassiveEffects @ react-dom.development.js:19724
(anonymous) @ react-dom.development.js:19621
workLoop @ scheduler.development.js:526
flushWork @ scheduler.development.js:488
performWorkUntilDeadline @ scheduler.development.js:141
Show 1 more frame
react.development.js:1279 Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.
    at resolveDispatcher (react.development.js:1279:1)
    at useEffect (react.development.js:1317:1)
    at x (index.js:142:1)
    at renderWithHooks (react-dom.development.js:12871:1)
    at mountIndeterminateComponent (react-dom.development.js:15201:1)
    at beginWork (react-dom.development.js:16138:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js:168:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:212:1)
    at invokeGuardedCallback (react-dom.development.js:260:1)
    at beginWork$1 (react-dom.development.js:20041:1)
resolveDispatcher @ react.development.js:1279
useEffect @ react.development.js:1317
x @ index.js:142
renderWithHooks @ react-dom.development.js:12871
mountIndeterminateComponent @ react-dom.development.js:15201
beginWork @ react-dom.development.js:16138
callCallback @ react-dom.development.js:168
invokeGuardedCallbackDev @ react-dom.development.js:212
invokeGuardedCallback @ react-dom.development.js:260
beginWork$1 @ react-dom.development.js:20041
performUnitOfWork @ react-dom.development.js:19162
workLoopSync @ react-dom.development.js:19141
performSyncWorkOnRoot @ react-dom.development.js:18811
(anonymous) @ react-dom.development.js:9782
unstable_runWithPriority @ scheduler.development.js:571
runWithPriority$1 @ react-dom.development.js:9737
flushSyncCallbackQueueImpl @ react-dom.development.js:9778
flushSyncCallbackQueue @ react-dom.development.js:9768
flushPassiveEffectsImpl @ react-dom.development.js:19774
unstable_runWithPriority @ scheduler.development.js:571
runWithPriority$1 @ react-dom.development.js:9737
flushPassiveEffects @ react-dom.development.js:19724
(anonymous) @ react-dom.development.js:19621
workLoop @ scheduler.development.js:526
flushWork @ scheduler.development.js:488
performWorkUntilDeadline @ scheduler.development.js:141

the component from our lib

/* eslint-disable react/jsx-no-bind */
import React, { useEffect } from 'react'
import ReactDOM from 'react-dom'
import  FocusLock  from 'react-focus-lock'
import { textPrimary } from '../../colors'
import { IconButton } from '../IconButton/IconButton'
import './AccessibleModal.scss'

export interface Props {
  children: React.ReactNode;
  extraInnerClass?: string;
  extraOuterClassName?: string;
  headerText: string;
  showCloseButton?: boolean;
  visible: boolean;
  lastIdFocused?: string;
  maxWidthClass?: string;
  hasBackButton?: boolean;
  titleText?: string;
  extraClassModal?: string;
  onClose: () => void;
  onBackButton?: () => void;
}

export const AccessibleModal: React.FC<Props> = ({
  headerText,
  children,
  extraInnerClass,
  extraOuterClassName,
  maxWidthClass,
  showCloseButton,
  visible,
  lastIdFocused,
  hasBackButton,
  titleText,
  extraClassModal,
  onClose,
  onBackButton
}) => {
  function onCloseAndRefocused() {
    if (lastIdFocused !== undefined && lastIdFocused !== '') {
      if (document.getElementById(lastIdFocused)) {
        (document.getElementById(lastIdFocused) as any).focus()
      }
    }
    onClose()
  }

  function onKeyDown(event: KeyboardEvent) {
    if (event && event.key === 'Escape' && visible) {
      onCloseAndRefocused()
    }
  }

  useEffect(() => document.removeEventListener('keydown', onKeyDown, false))

  useEffect(() => {
    if (visible) {
      document.body.style.overflow = 'hidden'
      document.addEventListener('keydown', onKeyDown, false)
    } else {
      document.body.style.overflow = 'unset'
    }
  }, [visible])

  const modal = (
    <FocusLock className={extraOuterClassName}>
      <div
        className="modal-overlay"
        onClick={onCloseAndRefocused}
        aria-hidden="true"
        role="dialog"
      />
      <div
        role="dialog"
        className={`accessible-modal ${extraClassModal} ${maxWidthClass}`}
        aria-modal
        aria-labelledby={headerText}
        tabIndex={-1}
      >
      <div className={`modal__inner ${maxWidthClass} ${extraInnerClass}`}>
        <div className="modal-container">
          <span className={hasBackButton ? '' : 'placeholder-span'}>
            {hasBackButton && (
              <IconButton
                aria-label="Voltar"
                variant="subtle"
                width="32px"
                height="32px"
                iconType="icon-Property-2Arrow---Left"
                iconSize="20px"
                iconColor={textPrimary}
                data-dismiss="modal"
                onClick={onBackButton}
              />
            )}
          </span>
          {titleText && (
            <p className="text-fontSmall text-center">
              {titleText}
            </p>
          )}
          <span className={showCloseButton ? '' : 'placeholder-span'}>
            {showCloseButton && (
              <IconButton
                aria-label="Fechar"
                variant="subtle"
                width="32px"
                height="32px"
                iconType="icon-Property-2Close"
                iconSize="20px"
                iconColor={textPrimary}
                data-dismiss="modal"
                onClick={onCloseAndRefocused}
              />
            )}
          </span>
        </div>
        {children}
      </div>
    </div>
  </FocusLock>
  )
  return visible ? ReactDOM.createPortal(modal, document.body) : null
}

how we're using it in the project

  return (
    <AccessibleModal
      extraOuterClassName="medical-record-modal"
      extraInnerClass={visible ? 'active' : 'inactive'}
      visible={visible}
      onClose={suppressModal}
      headerText="Finalizar atendimento"
      lastIdFocused={lastIdFocused}
      showCloseButton
      maxWidthClass="prenatal"
    >
      <img
        src={MedicalRecordWarningIcon}
        alt=""
        className="medical-record-icon"
        width={89}
        height={89}
      />
      <div className="text-center flex flex-col">
        <h4 className="mt-4 mb-4 text-center text-textPrimary text-fontSmall">
          {modalText()}
        </h4>
        <ul className="text-left ml-2 text-fontDefault text-textSecondary">
          {maternalDesireError
            ? <li>- desejo materno de via de parto</li>
            : null}
          {allRequired && pregnancyNotesError
            ? <li>- modificações no campo de observações da gravidez</li>
            : null}
          {requiredComorbidity
            ? <li>- comorbidades pré gestacionais</li>
            : null}
          {requiredIntercurrence
            ? <li>- intercorrências da gestação</li>
            : null}
        </ul>
        <p className="text-center mb-10 text-fontDefault text-textSecondary">
          Por favor, atualize os dados antes de finalizar a consulta para manter a equipe informada!
        </p>
      </div>
      <div>
        <div className="row justify-around items-center">
          <div className="pl-0 pr-0">
            <RegularButton
              type="button"
              label="Voltar e atualizar"
              extraClass="flex-1"
              variant="text"
              onClick={suppressModal}
            />
          </div>
          {!allRequired && (
            <div className="pr-0">
              <RegularButton
                type="button"
                label="Finalizar e assinar"
                extraClass="flex-1"
                onClick={onFinish}
              />
            </div>
          )}
        </div>
      </div>
    </AccessibleModal>
  )

I can't give you a codesandbox for example because the error only happens locally when we connect the library into the project using npm link

@theKashey
Copy link
Owner

  • onCloseAndRefocused is a little strange functionality. "return focus" is a built in feature, you don't need any extra management for it
  • useEffect(() => document.removeEventListener('keydown', onKeyDown, false)) has no deps and will be executed on every render. You want to return "cleanup effect" from the useEffect where you add handler
  useEffect(() => {
    if (visible) {
      document.body.style.overflow = 'hidden'
      document.addEventListener('keydown', onKeyDown, false);
+      return () => document.removeEventListener('keydown', onKeyDown, false);
    } else {
      document.body.style.overflow = 'unset'
    }
  }, [visible])
  • your variant of removing scroll is incorrect. Simple, working for the majority of cases, but still incorrect. Long story short please give a shot to react-focus-on - it does everything you might need in a "tested way", and it does some important things that your version does not.

How moment related to the focus-lock:

  • does anything changes when you don't use FocusLock (replace it by div)
    • probably yes. FocusLock is "broken"
  • does anything changes when you don't use createPortal (just return modal)
    • probably no
  • does anything changes when you don't use npm link
    • probably yes. npm link is a source of a problem.

npm link is a source of a problem - see facebook/react#13991 (comment)

There are multiple ways to resolve the issue, follow the linked issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants