Search code examples
reactjsmaterial-uireact-componentreact-hook-formreact-component-unmount

How can I fix the following warning: "Can't perform a React state update on an unmounted component"


Edit: I have been trying to implement a similar fix as this in my code, but I am very confused about how this method would translate in my code. I have been trying to apply that fix to my updateDose function, but it isn't working.

I am creating an app using React, Material UI, React Hook Form, and Yup.

I have two dialogs, one for the "edit dose" button and the "delete med" button for each card. On the "edit dose" dialog, the user inputs a new dose into a form.

I am getting the following warning if I try to update a medication's dose more than once (the first update shows no error, and then the second shows this)...

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

I have searched other tutorials to try to find a fix based on other examples, but I am not having any luck. Thank you so much in advance and here's my relevant code...

const Medication = ({medication}) => {
        const {handleSubmit, control, formState} = useForm({
            mode: "onChange",
            resolver: yupResolver(validationSchema)
        });

        // This handles the update medication dialog
        const [openUpdate, setOpenUpdate] = useState(false);
        const handleClickOpenUpdate = () => {
            setOpenUpdate(true);
        };
        const handleCloseUpdate = () => {
            setOpenUpdate(false);
        };

        // Function for the update dose button
        function updateDose(medicationId, parsedMedications, data) {
            let medication;
            let index;

            for (let i = 0; i < parsedMedications.length; i++) {
                if (parsedMedications[i].id === medicationId) {
                    medication = parsedMedications[i];
                    index = i;
                }
            }

            medication.dose = data.dose;
            parsedMedications[index] = medication;
            localStorage.setItem("medications", JSON.stringify(parsedMedications));

            // This forces the dialog to close
            setOpenUpdate(false);
        }

        return (
            <Box>
                <Card sx={cardSx}>
                    <CardContent>
                        <Typography sx={typographyMedicationSx} variant="h5">
                            Medication: {medication.medication}
                        </Typography>
                        <Typography sx={typographyMedicationSx} variant="h5">
                            Dose: {medication.dose} mg
                        </Typography>
                    </CardContent>
                    <Box>
                        <Button onClick={() => handleClickOpenUpdate()} size="large"
                                sx={buttonSx}
                                variant="contained">Edit
                            Dose</Button>
                        <Button onClick={() => handleClickOpen()} color="error"
                                size="large"
                                sx={buttonSx} variant="contained">Delete
                            Med </Button>
                    </Box>
                </Card>

                {/* Update medication dialog */}
                <Dialog
                    open={openUpdate}
                    onClose={handleCloseUpdate}
                    TransitionComponent={Transition}
                >
                    <DialogTitle sx={dialogTitleSx}>
                        {handleCloseUpdate ? (
                            <IconButton
                                aria-label="close"
                                onClick={handleCloseUpdate}
                                sx={iconButtonSx}
                            >
                                <CloseIcon/>
                            </IconButton>
                        ) : null}
                    </DialogTitle>

                    <form
                        onSubmit={handleSubmit((data) => updateDose(medication.id, parsed, data))}
                        noValidate>
                        <Typography sx={updateDoseTypographySx} variant="h4">
                            Update dose
                        </Typography>

                        <Box
                            sx={boxSx}
                        >
                            <Controller
                                name="dose"
                                control={control}
                                defaultValue={""}
                                render={({field: {ref, ...field}, fieldState: {error}}) => (
                                    <Autocomplete
                                        {...field}
                                        autoHighlight
                                        disableClearable
                                        isOptionEqualToValue={(option, value) => option.id === value.id}
                                        id="dose-autocomplete"
                                        onChange={(event, value) => field.onChange(value.label)}
                                        options={doseSuggestions}
                                        renderInput={(params) => (
                                            <TextField
                                                required
                                                error={!!error}
                                                helperText={error?.message}
                                                id="dose"
                                                label="Dose"
                                                name="dose"
                                                type="numeric"
                                                inputRef={ref}
                                                {...params}
                                            />
                                        )}
                                    />
                                )}
                            />

                            <Button disabled={!formState.isValid} size="large"
                                    sx={formButtonSx} type="submit"
                                    variant="contained">Submit</Button>
                        </Box>
                    </form>
                </Dialog>
            </Box>
        )
    };

    const medications = parsed.map((medication, index) => {
        return (<Medication medication={medication} key={"medication" + index}/>)
    });

Solution

  • Someone on a slack community helped me figure it out! I needed to add this to the dialog... keepMounted={true}