useEffect & map 超过了最大更新深度
Posted
技术标签:
【中文标题】useEffect & map 超过了最大更新深度【英文标题】:Maximum update depth exceeded with useEffect & map 【发布时间】:2021-06-30 21:04:55 【问题描述】:我在尝试设置表单错误对象时遇到了这个问题。基本上,我想在每个输入字段下方显示错误。作为回应,我得到了一个对象数组,如何设置我的错误对象?
错误 - 超出最大更新深度。当组件在 useEffect 中调用 setState 时,可能会发生这种情况,但 useEffect 要么没有依赖数组,要么每次渲染时其中一个依赖项都发生了变化。
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;
【问题讨论】:
我们需要看看 JSX,它会对我们有所帮助。 jsx 没有问题,只有第二个 useEffect 导致了这个问题,但我不知道如何解决它 【参考方案1】:你的第二个useEffect有问题,第一次更新你的状态userRegistrationError,组件重新渲染并重新执行了useEffect,因为依赖userRegistrationError 已更改,并且该过程再次重复,因为每次渲染都会更新状态。
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 ]); //replace userRegistrationError by setUserRegistrationError
【讨论】:
我得到了那部分,但我是新手,不知道如何解决它。我尝试在外面定义handleErrors,但它不起作用。你能帮忙吗? 看看我的答案 用 [errors, setUserRegistrationError] 替换 [errors, userRegistrationError] 我没有看到,但它不起作用 eslint 正在抱怨。我正在添加整个代码 忽略 eslint 并使用 [errors, setUserRegistrationError] 测试您的代码。 我已经尝试过您的编辑,但它现在无法正常运行,它没有无限运行,而是设置了最后一个错误值,即电子邮件而不是名字和电子邮件【参考方案2】:您的效果取决于userRegistrationError
,它是一个基于引用的对象。每次useEffect
运行,setUserRegistrationError
创建一个新的对象引用,这会导致无限循环,因为引用不会与前一个相同。
避免此问题并保留正确引用的一种方法是将回调函数传递给setUserRegistrationError
,而不是传递一个值。这样userRegistrationError
就不再是依赖项了,而是作为函数的参数:
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]);
【讨论】:
我已经尝试过您的编辑,但它现在无法正常运行,它没有无限运行,而是设置了最后一个错误值,即电子邮件而不是名字和电子邮件 我认为问题是名字应该是驼峰式firstName
给定您的输入,对吗? fwiw 你应该使用forEach
而不是map
当你只是迭代一个数组时
参数没有问题看起来像在 set 函数中我必须这样写 (userRegistrationError) => ( ...userRegistrationError, firstNameError: error.msg, )
@NeerajMishra 很好,但这正是我首先发布答案的方式......
我在回答时告诉过要传递一个函数(它也在代码中),我相信你可能没有看到以上是关于useEffect & map 超过了最大更新深度的主要内容,如果未能解决你的问题,请参考以下文章
使用 useEffect 钩子在反应功能组件中未使用 .map() 显示的元素