reactjs / react-modal

Accessible modal dialog component for React
http://reactcommunity.org/react-modal
MIT License
7.35k stars 809 forks source link

'ReactModal' cannot be used as a JSX component. #960

Open bokibo opened 2 years ago

bokibo commented 2 years ago

Hi,

I am using this component for certain and it works perfect, don't have any problems. But yesterday I tried to update react from v17 to v18 and after update I got this error

'ReactModal' cannot be used as a JSX component.

Is there any way to fix it so I could continue using this component or this is caused by new react version and fix is needed? So far I did revert to react v17 and it works but in the future I would like to update to version 18.

Thank you

btraut commented 1 year ago

I'm also encountering this. It seems to work fine with React 18, so here's my current workaroud:

import Modal from 'react-modal';
import { ComponentType } from 'react';

…

const ModalSafeForReact18 = Modal as ComponentType<ReactModal['props']>;

return <ModalSafeForReact18>…</ModalSafeForReact18>;
diasbruno commented 1 year ago

@btraut @bokibo Can you provide the actual error that this version is throwing, please?

btraut commented 1 year ago
  Its instance type 'Modal' is not a valid JSX element.
    The types returned by 'render()' are incompatible between these types.
      Type 'import("<my app's path>/node_modules/@types/react-dom/node_modules/@types/react/index").ReactNode' is not assignable to type 'React.ReactNode'.
        Type '{}' is not assignable to type 'ReactNode'.

88     <Modal
        ~~~~~~~~~~

From what I can tell from my yarn.lock, it looks like "@types/react": "*" is resolving to version 17.0.34 and my app ("@types/react": "^18.0.20") is resolving to 18.0.20. Those are likely incompatible.

diasbruno commented 1 year ago

This is a typescript thing...it's very sad. I don't know how the react version is injected on the react-modal type definition, so I can't help.

If you want to give it a try here is type definitions DefinitelyTyped/react-modal. It must be easy, but I don't have much time to work on this.

diasbruno commented 1 year ago

I'm flaging as 'not a bug', because it isn't really a bug, but a type definition problem that affects only typescript projects.

diasbruno commented 1 year ago

Or...you can open a bug on the type repository and mention me, so I can follow up.

memark commented 1 year ago

I'm using react-modal 3.15.1 with @types/react-modal 3.13.1, react 18.2.0 and typescript 4.8.4 without any error like that. Could you please provide some actual code or a codepen/playground that gives this error, @bokibo?

NathanVss commented 1 year ago

I'm having the same problem :/

NathanVss commented 1 year ago

I have the solution.

I realized that some of the packages were still using @types/react@17.0.44 , including the react-modal one:

yarn list --pattern "@types/react"

├─ @types/react-dom@18.0.7
│  └─ @types/react@17.0.44
├─ @types/react-modal@3.13.1
│  └─ @types/react@17.0.44
├─ @types/react@18.0.22

This was caused by two things:

    "dependencies": {
        "@types/react": "*"
    },
  "resolutions": {
    "@types/react": "17.0.44"
  }

Solution:

Hope this helps ! 😗

diasbruno commented 1 year ago

@NathanVss That's awesome.

As this is related to types, this package is not the one to blame.

This problem needs to be opened on https://github.com/DefinitelyTyped/DefinitelyTyped.

bokibo commented 1 year ago

Hi everyone. Thanks for the answers. I haven't had time to respond so I did a rollback to react 17. Today I had some free time I tried again and I get the same error. I agree it is a typescript thing but could someone help me solve it?

This is the component that uses Reactmodal. I did a thing with ModalSafeForReact18 that @btraut mentioned.

import React, {useEffect, useLayoutEffect, useState} from "react";
import ReactModal from "react-modal";
import {IAlertModalProps} from "./alertModal.props";
import {Overlay} from "../modal/Modal.style";
import AlertModalContainer from "./alert-modal-container/AlertModalContainer";
import Modal from 'react-modal';
import { ComponentType } from 'react';

ReactModal.setAppElement('#root');
const ModalSafeForReact18 = Modal as ComponentType<ReactModal['props']>;

const AlertModal: React.FC<React.PropsWithChildren<IAlertModalProps>> =
    ({isOpen, type, afterOpen, afterClose, closeModal, children}) => {
    return <ModalSafeForReact18
        isOpen={isOpen}
        onAfterOpen={onOpen}
        onAfterClose={onClose}
        contentElement={(props) =>
            <AlertModalContainer
                type={type}
                onClose={closeModal}
                {...props}>
                {children}
            </AlertModalContainer>}
        overlayElement={(props, contentElement) =>
            <Overlay {...props}>{contentElement}</Overlay>}
        overlayClassName={'overlay'}
    />
}
export default AlertModal

And the AlertModalContainer component

import React from "react";
import {Wrapper, CloseButton, Title, Icon} from "./AlertModalContainer.style";
import {AlertModalType} from "../alertModal.props";
interface IAlertModalContainerProps {
    type: AlertModalType,
    onClose: () => void
}
const AlertModalContainer: React.FC<IAlertModalContainerProps> =
    React.forwardRef<HTMLDivElement, IAlertModalContainerProps>(({type, onClose, children}) => {
    return <Wrapper>
        <Icon type={type}>{type === 'success' ?
            <i className={'ico-Check'}/> : type === 'error' ?
                <i className={'ico-Exit-outlined'}/> :
                <i className={'ico-Information-outlined'}/>}</Icon>
        <Title type={type}>{type === 'success' ? 'Success!' : type === 'error' ? 'Failure!' : 'Almost Done!'}</Title>
        {children}
        <CloseButton onClick={() => onClose()}>{type === 'success' ? 'Done' : 'Close'}</CloseButton>
    </Wrapper>
})
export default AlertModalContainer

And these are two errors I get.

on AlertModalContainer inside AlertModal component

on children in AlertModalContainer component

When I add children type to IAlertModalContainerProps as ReactNode then on the "overlayElement" I get

And when I change children to ReactElement then I get this error on children on AlertModalContainter

Could someone please help?

kento-hamaguchi-md commented 1 year ago

Excuse my poor English.

Component in React 18 can no longer pass children implicitly. To do this, we need to add a children field of type React.ReactNode to Props.

Therefore, to extend the react-modal's Modal type in the project, the following type definition was written. This allows the Modal Component to specify a child component. What do you think?

(react-modal.d.ts)

import React from 'react'
import { OriginalProps } from 'react-modal'

declare module 'react-modal' {
    interface Props extends OriginalProps {
        children?: React.ReactNode
    }
}
diasbruno commented 1 year ago

@kento-hamaguchi-md Interesting...is this type definition also valid for React <18?

kento-hamaguchi-md commented 1 year ago

@diasbruno Yes. My project is React 18. I am able to build and run it without any problems. However, this is not an essential solution, but an interim method.

I am sorry if this is not the cause of the issue in this Issue.

kento-hamaguchi-md commented 1 year ago

Sorry, the check on the VSCode editor seemed to resolve the problem, but in the actual build process, the problem continued. I will look into it again to see if the type definition is not passing or if there are other underlying issues.

diasbruno commented 1 year ago

@kento-hamaguchi-md That's unfortunate, but great idea.This actually lines up with @bokibo's comment.

You can find more information on https://github.com/DefinitelyTyped/DefinitelyTyped for the react-modal's type definition.

diasbruno commented 1 year ago

Keep me posted with your findings and I'll be happy to help.

kento-hamaguchi-md commented 1 year ago

I have tried many things, but adding this setting to tsconfig.json will make the type definitions strict at build time.

The type definition file that overrides the react-modal definition should be placed in src/types/global/react-modal.d.ts . It seems to be the specification that it should not be placed directly in types, but through, for example, global.

{
  "compilerOptions": {
    "typeRoots": ["src/types"],

However, after doing this and reviewing the Next.js Lint settings, etc., the build now passes even if the typeRoots setting is turned off. I am sorry, but I could not find out exactly what caused the problem.

However, as for the type definition issue, the aforementioned react-modal.d.ts seems to work fine. I guess it depends on the environment of each project. Sorry for the reference, but the above is the result of our investigation.

diasbruno commented 1 year ago

Bom trabalho! This one is actually annoying as hell.

Here is a checklist:

chinanderm commented 1 year ago

Things that resolved it for me with React 18:

dcristafovici commented 1 year ago

@chinanderm thank you, man. I tried before only to update to the latest version, but removing and installing all deps helped me.

diasbruno commented 1 year ago

@denibudeyko Would you like add this note on the readme? Well, it seems the way to go...

Namone commented 1 year ago

I had luck updating my .tsconfig.json in my project to the following:

{
  "compilerOptions": {
    "target": "es2020",
    "lib": [
      "dom",
      "dom.iterable"
    ],
    "paths": {
      "react": [ "./node_modules/@types/react" ],
      "dompurify": [ "./node_modules/@types/dompurify" ]
    },
    "strict": true,
    "jsx": "react",
  },
  "include": [
    "src/**/*"
, "tests"  ]
}

I previously had compilerOptions.jsx set to react-jsx; simply renaming it to react seemed to resolve the problem. Also being sure to import the relevant items under paths.

After that, I was able to build:

  115 B      build/1abe08b3249335736c0f016631f03702.js
  115 B      build/1d48b3a38a76bfc80d5718a91fd4c252.js
  115 B      build/23e579d49d8f8206607964d627b336b7.js
  115 B      build/49db9cf6f30a219cf140f7846d87a418.js
  115 B      build/a1883a50fa7e229ceeb72b409367a1b1.js
  115 B      build/b3c8fac34d63a9fe758b737a2560c911.js
  115 B      build/bf24eb9b91f882cafcfa6a7e8e3c56b1.js
  114 B      build/1046b30afca9b1942dd448bcafff2a95.js
  114 B      build/eab387dee57def86c245a3d71365a614.js
  114 B      build/f574c6ed6a178b4374b7c570ab2fce5f.js
  113 B      build/c98e74fc97b04fe8bf43dcdff549afcf.js
  112 B      build/0ffb18fb70c87335edee31a479f58a43.js

The bundle size is significantly larger than recommended.
Consider reducing it with code splitting: https://goo.gl/9VhYWB
You can also analyze the project dependencies: https://goo.gl/LeUzfb

The project was built assuming it is hosted at /.
You can control this with the homepage field in your package.json.

The build folder is ready to be deployed.
You may serve it with a static server:

  npm install -g serve
  serve -s build

Find out more about deployment here:

  https://cra.link/deployment

My package.json:

"dependencies": {
    "@auth0/auth0-react": "^2.1.1",
    "@ctrl/tinycolor": "^3.6.0",
    "@emotion/react": "^11.7.1",
    "@emotion/styled": "^11.6.0",
    "@hookform/resolvers": "^3.1.0",
    "@material-ui/utils": "^4.11.3",
    "@mui/icons-material": "^5.13.7",
    "@mui/material": "^5.0.6",
    "@mui/styles": "^5.13.7",
    "@mui/utils": "^5.13.1",
    "@mui/x-data-grid": "^5.6.0",
    "@testing-library/jest-dom": "^5.14.1",
    "@types/dompurify": "^3.0.2",
    "@types/node": "^16.11.25",
    "@types/react-dom": "^17.0.10",
    "@types/react-modal": "^3.13.1",
    "ace-builds": "^1.4.14",
    "dompurify": "^3.0.4",
    "history": "^5.3.0",
    "jss": "^10.10.0",
    "jss-preset-default": "^10.10.0",
    "moment": "^2.29.4",
    "mui-color-input": "^1.1.0",
    "react": "^18.2.0",
    "react-ace": "^9.5.0",
    "react-dom": "*",
    "react-hook-form": "^7.45.1",
    "react-modal": "^3.14.4",
    "react-quill": "^2.0.0",
    "react-router-dom": "^6.14.1",
    "react-scripts": "5.0.0",
    "style-loader": "^3.3.2",
    "typescript": "^4.4.4",
    "uuid": "^9.0.0",
    "uuidv4": "^6.2.13",
    "web-vitals": "^2.1.4",
    "yup": "^1.2.0"
  },