Skip to content
Advertisement

Maximum update depth exceeded with useEffect & map

I am facing this when I am trying to set form error object. Basically, I want to show the errors below each input field. In response, I am getting an array of objects how do I set to my error object?

Error – Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn’t have a dependency array, or one of the dependencies changes on every render.

import axios from "axios";
import React, { useState, useEffect, useCallback } from "react";
import { Link } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { register } from "../actions/userActions";
const Register = () => {
    const [countriesList, setCountriesList] = useState("");
    const [userRegistration, setUserRegistration] = useState({
        firstName: "",
        lastName: "",
        email: "",
        password: "",
        fullAddress: "",
        city: "",
        zipCode: "",
        country: "",
        phone: "",
        terms: true,
    });
    const [userRegistrationError, setUserRegistrationError] = useState({
        firstNameError: "",
        lastNameError: "",
        emailError: "",
        passwordError: "",
        fullAddressError: "",
        cityError: "",
        zipCodeError: "",
        countryError: "",
        phoneError: "",
        termsError: "",
    });
    const dispatch = useDispatch();

    const userRegister = useSelector((state) => state.userRegister);
    const { loading, errors, success } = userRegister;

    useEffect(() => {
        const countries = async () => {
            try {
                const { data } = await axios.get(
                    `https://restcountries.eu/rest/v2/all`
                );
                setCountriesList(data);
            } catch (err) {
                console.error(err);
            }
        };
        countries();
    }, []);

    useEffect(() => {
        const handleErrors = (errors) => {
            errors.map((error) => {
                if (error.param === "firstname") {
                    setUserRegistrationError({
                        ...userRegistrationError,
                        firstNameError: error.msg,
                    });
                }
                if (error.param === "email") {
                    setUserRegistrationError({
                        ...userRegistrationError,
                        emailError: error.msg,
                    });
                }
                return null;
            });
        };
        if (errors) {
            handleErrors(errors);
        }
    }, [errors, setUserRegistrationError]);

    const handleChange = (e) => {
        const name = e.target.name;
        const value = e.target.value;
        setUserRegistration({ ...userRegistration, [name]: value });
    };
    const handleChkChange = (e) => {
        const checked = e.target.checked;
        console.log(checked);
        setUserRegistration({ ...userRegistration, terms: checked });
    };

    const handleSubmit = (e) => {
        e.preventDefault();
        try {
            dispatch(register());
        } catch (error) {
            console.error(error);
        }
    };
    return (
        <div className="form_container">
            <form action="" onSubmit={handleSubmit}>
                <div className="row no-gutters">
                    <div className="col-6 pr-1">
                        <div className="form-group">
                            <div className="form-group">
                                <input
                                    type="text"
                                    name="firstName"
                                    className="form-control"
                                    placeholder="First Name*"
                                    value={userRegistration.firstName}
                                    onChange={handleChange}
                                />
                                <p className="form-vald-error">
                                    {userRegistrationError.firstNameError &&
                                        userRegistrationError.firstNameError}
                                </p>
                            </div>
                        </div>
                    </div>
                    <div className="col-6 pr-1">
                        <div className="form-group">
                            <input
                                type="text"
                                className="form-control"
                                name="lastName"
                                placeholder="Last Name*"
                                value={userRegistration.lastName}
                                onChange={handleChange}
                            />
                            <p className="form-vald-error">
                                {userRegistrationError.lastNameError &&
                                    userRegistrationError.lastNameError}
                            </p>
                        </div>
                    </div>
                </div>
                <hr />
                <div className="private box">
                    <div className="row no-gutters">
                        <div className="col-6 pr-1">
                            <div className="form-group">
                                <input
                                    type="email"
                                    className="form-control"
                                    name="email"
                                    id="email_2"
                                    placeholder="Email*"
                                    value={userRegistration.email}
                                    onChange={handleChange}
                                />
                                <p className="form-vald-error">
                                    {userRegistrationError.emailError &&
                                        userRegistrationError.emailError}
                                </p>
                            </div>
                        </div>
                        <div className="col-6 pl-1">
                            <div className="form-group">
                                <input
                                    type="password"
                                    className="form-control"
                                    name="password"
                                    id="password_in_2"
                                    placeholder="Password*"
                                    value={userRegistration.password}
                                    onChange={handleChange}
                                />
                                <p className="form-vald-error">
                                    {userRegistrationError.passwordError &&
                                        userRegistrationError.passwordError}
                                </p>
                            </div>
                        </div>
                        <div className="col-12">
                            <div className="form-group">
                                <input
                                    type="text"
                                    name="fullAddress"
                                    className="form-control"
                                    placeholder="Full Address*"
                                    value={userRegistration.fullAddress}
                                    onChange={handleChange}
                                />
                                <p className="form-vald-error">
                                    {userRegistrationError.fullAddressError &&
                                        userRegistrationError.fullAddressError}
                                </p>
                            </div>
                        </div>
                    </div>
                    {/* /row */}
                    <div className="row no-gutters">
                        <div className="col-6 pr-1">
                            <div className="form-group">
                                <input
                                    type="text"
                                    className="form-control"
                                    placeholder="City*"
                                    name="city"
                                    value={userRegistration.city}
                                    onChange={handleChange}
                                />
                                <p className="form-vald-error">
                                    {userRegistrationError.cityError &&
                                        userRegistrationError.cityError}
                                </p>
                            </div>
                        </div>
                        <div className="col-6 pl-1">
                            <div className="form-group">
                                <input
                                    type="text"
                                    className="form-control"
                                    placeholder="Postal Code*"
                                    name="zipCode"
                                    value={userRegistration.zipCode}
                                    onChange={handleChange}
                                />
                                <p className="form-vald-error">
                                    {userRegistrationError.zipCodeError &&
                                        userRegistrationError.zipCodeError}
                                </p>
                            </div>
                        </div>
                    </div>
                    {/* /row */}
                    <div className="row no-gutters">
                        <div className="col-6 pr-1">
                            <div className="form-group">
                                <div className="custom-select-form">
                                    <select
                                        className="wide add_bottom_10 form-control"
                                        name="country"
                                        id="country"
                                        value={userRegistration.country}
                                        onChange={handleChange}
                                    >
                                        <option>Country*</option>
                                        {countriesList &&
                                            countriesList.map((country) => (
                                                <option
                                                    key={country.alpha2Code}
                                                    value={country.alpha2Code}
                                                >
                                                    {country.name}
                                                </option>
                                            ))}
                                    </select>
                                    <p className="form-vald-error">
                                        {userRegistrationError.countryError &&
                                            userRegistrationError.countryError}
                                    </p>
                                </div>
                            </div>
                        </div>
                        <div className="col-6 pl-1">
                            <div className="form-group">
                                <input
                                    type="text"
                                    className="form-control"
                                    placeholder="Telephone *"
                                    name="phone"
                                    value={userRegistration.phone}
                                    onChange={handleChange}
                                />
                                <p className="form-vald-error">
                                    {userRegistrationError.phoneError &&
                                        userRegistrationError.phoneError}
                                </p>
                            </div>
                        </div>
                    </div>
                    {/* /row */}
                </div>
                <hr />
                <div className="form-group">
                    <label className="container_check">
                        Accept <Link to="#0">Terms and conditions</Link>
                        <input
                            type="checkbox"
                            name="terms"
                            checked={userRegistration.terms}
                            onChange={handleChkChange}
                        />
                        <span className="checkmark" />
                        <p className="form-vald-error">
                            {userRegistrationError.termsError &&
                                userRegistrationError.termsError}
                        </p>
                    </label>
                </div>
                <div className="text-center">
                    <input
                        type="submit"
                        defaultValue="Register"
                        className="btn_1 full-width"
                    />
                </div>
            </form>
        </div>
    );
};

export default Register;

Advertisement

Answer

your effect depends on userRegistrationError which is an object, reference based. Each time useEffect runs,setUserRegistrationError creates a new object reference, which leads to an infinite loop since references won’t be the same as the previous one.

One approach to avoid this issue and keep the right references, is to pass a callback function to setUserRegistrationError instead than a value. This way userRegistrationError is no longer a dependency, it will be an argument to your function instead:

useEffect(() => {
    const handleErrors = (errors) => {
        errors.forEach((error) => {
            if (error.param === "firstName") {
                // here you pass a callback function instead, and userRegistrationError is no longer a dependency
                // and returns the next state as expected
                setUserRegistrationError(userRegistrationError => ({
                    ...userRegistrationError,
                    firstNameError: error.msg,
                }));
            }
            if (error.param === "email") {
                setUserRegistrationError(userRegistrationError => ({
                    ...userRegistrationError,
                    emailError: error.msg,
                }));
            }
        });
    };
    if (errors) {
        handleErrors(errors);
    }
}, [errors, setUserRegistrationError]);
Advertisement