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]);