nextui-org / nextui

🚀 Beautiful, fast and modern React UI library.
https://nextui.org
MIT License
21.8k stars 1.49k forks source link

[BUG] - THE CLOSING OF THE MODAL IS PROPAGATING #3931

Open Mateusvaz89 opened 2 hours ago

Mateusvaz89 commented 2 hours ago

NextUI Version

2.4.8

Describe the bug

When an open modal is closed and a new modal is opened afterward, the close event from the first modal is improperly propagating to the second one. This causes the new modal to open immediately and then close automatically, resulting in unexpected behavior.

Your Example Website or App

No response

Steps to Reproduce the Bug or Issue

-------------------- FIRST MODAL --------------------- import { Button, FormControl, FormHelperText, FormLabel, LinearProgress, Typography, } from '@mui/joy' import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Avatar, useDisclosure, } from '@nextui-org/react'

import { Select as SelectNext, SelectItem as SelectItemNext, Input as InputNext, } from '@nextui-org/react'

import { FormProps, schema } from './form-schema' import { Controller, useForm } from 'react-hook-form' import { useContext, useEffect, useMemo } from 'react'

import { UserContext } from '@/context/user-context' import { useMediaQuery } from 'react-responsive' import { zodResolver } from '@hookform/resolvers/zod' import { auth } from '@/config/firebase-config' import { useBrands } from './hooks/use-load-brands' import { useModels } from './hooks/use-load-models' import { useVersions } from './hooks/use-load-versions' import { userRegisterCar } from './hooks/use-register-car' import { format, subYears } from 'date-fns' import { toast } from 'sonner' import { UserCarsContext } from '@/context/user-cars-context' import { RequestCar } from './request-car'

export function RegisterCar({ buttonProps }: { buttonProps?: any }) { const { isOpen, onOpen, onOpenChange } = useDisclosure()

const { control, handleSubmit, watch, reset } = useForm<FormProps>({
    resolver: zodResolver(schema),
    defaultValues: {
        brandId: '',
        modelId: '',
        versionId: '',
        licensePlate: '',
        mileage: 0,
        year: '',
    },
})

const { getCars } = useContext(UserCarsContext)
const { firebaseUser } = useContext(UserContext)
const { fetchBrands, brands, isLoading: isLoadingBrands } = useBrands()
const { fetchModels, models, isLoading: isLoadingModels } = useModels()
const {
    fetchVersions,
    versions,
    isLoading: IsLoadingVersions,
} = useVersions()

const registerCar = userRegisterCar(() => {
    reset()
    onOpenChange()
    toast.success('carro adicionado!')
    getCars!()
})
const brandId = watch('brandId')
const modelId = watch('modelId')
const isMobile = useMediaQuery({ query: '(max-width: 640px)' })

useMemo(() => {
    auth.onAuthStateChanged((user) => {
        if (user) {
            fetchBrands()
        }
    })
}, [])

useEffect(() => {
    if (brandId) {
        fetchModels(brandId)
    }
}, [brandId])

useEffect(() => {
    if (modelId) fetchVersions(modelId)
}, [modelId])

useEffect(() => {
    reset()
}, [open])

return (
    <>
        <Button
            onClick={onOpen}
            size="sm"
            variant="outlined"
            disabled={firebaseUser === undefined}
            {...buttonProps}
        >
            Adicionar
        </Button>
        <Modal
            id="register-car-modal"
            size={isMobile ? 'lg' : 'md'}
            isOpen={isOpen}
            onClose={onOpenChange}
        >
            <ModalContent>
                <ModalHeader>
                    <div className="flex items-center gap-3">
                        <Typography fontWeight={600} level="title-md">
                            Adicionar carro
                        </Typography>
                    </div>
                </ModalHeader>

                <ModalBody className="pb-4">
                    <form
                        id="create-car-form"
                        onSubmit={(e) => {
                            e.preventDefault()
                            e.stopPropagation()
                            handleSubmit(registerCar.execute)(e)
                        }}
                        className="flex flex-col  gap-6"
                    >
                        <div className="-my-2 h-2">
                            {isLoadingBrands ||
                                isLoadingModels ||
                                (IsLoadingVersions && (
                                    <LinearProgress size="sm" />
                                ))}
                        </div>
                        <div className="grid grid-cols-2 gap-6">
                            <Controller
                                control={control}
                                name="brandId"
                                render={({
                                    field,
                                    fieldState: { error },
                                }) => (
                                    <FormControl
                                        error={error !== undefined}
                                    >
                                        <FormLabel>Marca</FormLabel>
                                        <SelectNext
                                            variant="bordered"
                                            placeholder="selecione"
                                            {...field}
                                            disabled={
                                                brands.length === 0 ||
                                                registerCar.isLoading
                                            }
                                        >
                                            {brands.map((brand) => (
                                                <SelectItemNext
                                                    key={brand.id}
                                                    value={brand.id}
                                                >
                                                    {brand.name}
                                                </SelectItemNext>
                                            ))}
                                        </SelectNext>
                                        <FormHelperText>
                                            {error?.message}
                                        </FormHelperText>
                                    </FormControl>
                                )}
                            />
                            <Controller
                                control={control}
                                name="modelId"
                                render={({
                                    field,
                                    fieldState: { error },
                                }) => (
                                    <FormControl
                                        error={error !== undefined}
                                    >
                                        <FormLabel>Modelo</FormLabel>
                                        <SelectNext
                                            variant="bordered"
                                            {...field}
                                            disabled={
                                                brandId == '' ||
                                                brandId == null ||
                                                registerCar.isLoading
                                            }
                                            placeholder="selecione"
                                        >
                                            {models.map((model) => (
                                                <SelectItemNext
                                                    key={model.id}
                                                    value={model.id}
                                                    startContent={
                                                        <Avatar
                                                            alt={model.name}
                                                            className="h-6 w-6"
                                                            src={
                                                                model.imageURL
                                                            }
                                                        />
                                                    }
                                                >
                                                    {model.name}
                                                </SelectItemNext>
                                            ))}
                                        </SelectNext>
                                        <FormHelperText>
                                            {error?.message}
                                        </FormHelperText>
                                    </FormControl>
                                )}
                            />
                        </div>
                        <Controller
                            control={control}
                            name="versionId"
                            render={({ field, fieldState: { error } }) => (
                                <FormControl error={error !== undefined}>
                                    <FormLabel>Versão</FormLabel>
                                    <SelectNext
                                        variant="bordered"
                                        disabled={
                                            modelId === '' ||
                                            modelId == null ||
                                            registerCar.isLoading
                                        }
                                        {...field}
                                        placeholder="selecione a versão"
                                    >
                                        {versions.map((version) => (
                                            <SelectItemNext
                                                key={version.id}
                                                value={version.id}
                                            >
                                                {version.name}
                                            </SelectItemNext>
                                        ))}
                                    </SelectNext>
                                    <FormHelperText>
                                        {error?.message}
                                    </FormHelperText>
                                </FormControl>
                            )}
                        />
                        <div className="grid grid-cols-2 gap-6">
                            <Controller
                                control={control}
                                name="year"
                                render={({
                                    field,
                                    fieldState: { error },
                                }) => (
                                    <FormControl
                                        error={error !== undefined}
                                    >
                                        <FormLabel>Ano do modelo</FormLabel>
                                        <SelectNext
                                            {...field}
                                            variant="bordered"
                                            placeholder="selecione"
                                            disabled={registerCar.isLoading}
                                        >
                                            {Array.from({ length: 30 }).map(
                                                (_, index) => {
                                                    const year = format(
                                                        subYears(
                                                            new Date(),
                                                            index
                                                        ),
                                                        'yyyy'
                                                    )
                                                    return (
                                                        <SelectItemNext
                                                            key={year}
                                                            value={year}
                                                        >
                                                            {year}
                                                        </SelectItemNext>
                                                    )
                                                }
                                            )}
                                        </SelectNext>
                                        <FormHelperText>
                                            {error?.message}
                                        </FormHelperText>
                                    </FormControl>
                                )}
                            />
                            <Controller
                                control={control}
                                name="licensePlate"
                                render={({
                                    field,
                                    fieldState: { error },
                                }) => (
                                    <FormControl
                                        error={error !== undefined}
                                    >
                                        <FormLabel>Placa</FormLabel>
                                        <InputNext
                                            variant="bordered"
                                            placeholder="ABC-1234"
                                            {...field}
                                            disabled={registerCar.isLoading}
                                        />
                                        <FormHelperText>
                                            {error?.message}
                                        </FormHelperText>
                                    </FormControl>
                                )}
                            />
                        </div>
                        <Controller
                            control={control}
                            name="mileage"
                            render={({ field, fieldState: { error } }) => (
                                <FormControl error={error !== undefined}>
                                    <FormLabel>Quilometragem</FormLabel>
                                    <InputNext
                                        variant="bordered"
                                        placeholder="000"
                                        {...field}
                                        value={String(field.value)}
                                        disabled={registerCar.isLoading}
                                    />
                                    <FormHelperText>
                                        {error?.message}
                                    </FormHelperText>
                                </FormControl>
                            )}
                        />
                    </form>
                </ModalBody>

                <ModalFooter className="gap-6 border-t bg-zinc-50">
                    <RequestCar onOpenModal={onOpenChange} />
                    <Button
                        loading={registerCar.isLoading}
                        type="submit"
                        form="create-car-form"
                    >
                        Adicionar
                    </Button>
                </ModalFooter>
            </ModalContent>
        </Modal>
    </>
)

}

-------------------- SECOND MOL ---------------------

import { Button, FormControl, FormHelperText, FormLabel, Link, Typography, } from '@mui/joy' import { Input, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, } from '@nextui-org/react' import { IconInfoCircle } from '@tabler/icons-react' import { Fragment, useState } from 'react' import { Controller, useForm } from 'react-hook-form' import { FormProps, schema } from './form-schema' import { zodResolver } from '@hookform/resolvers/zod'

export function RequestCar(props: { onOpenModal: () => void }) { const [openModal, setOpenModal] = useState(false)

function handleOpenModal() {
    props.onOpenModal()

    setOpenModal(true)
}

function handleCloseModal() {
    setOpenModal(false)
}

const { control, handleSubmit } = useForm<FormProps>({
    resolver: zodResolver(schema),
})

return (
    <Fragment>
        <Link
            color="neutral"
            startDecorator={<IconInfoCircle size={16} />}
            onClick={handleOpenModal}
            sx={{ fontSize: 'sm' }}
        >
            Não econtrei meu carro
        </Link>
        <Modal
            isOpen={openModal}
            onClose={() => {
                handleCloseModal()
            }}
        >
            <ModalContent>
                <ModalHeader className="flex flex-col">
                    <Typography fontWeight={600} level="title-md">
                        Solicitar cadastro de modelo
                    </Typography>
                    <Typography fontWeight={400} level="body-sm">
                        Preecha os campos abaixo para solicitar o cadastro
                        de modelo
                    </Typography>
                </ModalHeader>
                <ModalBody className="pb-4">
                    <form className="flex flex-col gap-6">
                        <Controller
                            control={control}
                            name="brand"
                            render={({ field, fieldState: { error } }) => (
                                <FormControl error={error !== undefined}>
                                    <FormLabel>Nome da marca</FormLabel>
                                    <Input
                                        variant="bordered"
                                        placeholder="digite o nome da marca"
                                        {...field}
                                    />
                                    <FormHelperText>
                                        {error?.message}
                                    </FormHelperText>
                                </FormControl>
                            )}
                        />
                        <Controller
                            control={control}
                            name="model"
                            render={({ field, fieldState: { error } }) => (
                                <FormControl error={error !== undefined}>
                                    <FormLabel>Nome do modelo</FormLabel>
                                    <Input
                                        variant="bordered"
                                        placeholder="digite o nome do modelo"
                                        {...field}
                                    />
                                    <FormHelperText>
                                        {error?.message}
                                    </FormHelperText>
                                </FormControl>
                            )}
                        />
                        <Controller
                            control={control}
                            name="version"
                            render={({ field, fieldState: { error } }) => (
                                <FormControl error={error !== undefined}>
                                    <FormLabel>Nome da versão</FormLabel>
                                    <Input
                                        variant="bordered"
                                        placeholder="digite o nome da versão"
                                        {...field}
                                    />
                                    <FormHelperText>
                                        {error?.message}
                                    </FormHelperText>
                                </FormControl>
                            )}
                        />
                    </form>
                </ModalBody>
                <ModalFooter className="gap-6 border-t bg-zinc-50">
                    <Button>Solicitar</Button>
                </ModalFooter>
            </ModalContent>
        </Modal>
    </Fragment>
)

}

Expected behavior

Do not propagate the state change from one modal to another.

Screenshots or Videos

ezgif-1-b22f50d373

Operating System Version

windows

Browser

Chrome

linear[bot] commented 2 hours ago

ENG-1486 [BUG] - THE CLOSING OF THE MODAL IS PROPAGATING

wingkwong commented 2 hours ago

please provide a sandbox instead. It's hard to debug just based on the given code.

Mateusvaz89 commented 1 hour ago

@wingkwong https://codesandbox.io/p/github/Mateusvaz89/nextui-modal-bug/main?import=true