React发送邮件表单选择下拉选项后出现无限重渲染及API调用问题
问题原因
emailClick()、changeDate()两个请求函数直接放在组件顶层执行,组件每次渲染都会调用这两个函数,函数内部调用setCemail、setDate更新状态又触发重新渲染,形成无限循环。- useEffect用法错误:React的useEffect仅支持2个参数(回调函数+单个数组格式的依赖项),你写了
[date]、[cemail]两个依赖数组,实际仅第一个[date]生效,导致初始拉取数据的逻辑会在每次date变化时重复执行,进一步加剧循环。 - 下拉选中后的API触发逻辑没有和对应状态绑定,无法做到仅选中值变化时才调用接口。
修复方案
- 移除组件顶层直接调用的
emailClick()、changeDate(),将两个请求分别封装到对应useEffect中,绑定clientId、clientname和hid作为依赖,仅当选中的客户端/假期变更且有有效值时才触发请求。 - 修正初始拉取数据的useEffect依赖项为
[],保证仅在组件首次挂载时执行一次拉取假期、客户端、邮件模板的逻辑。
修改后完整代码:
import Select from "react-select"; import Axios from "axios"; import React, { useState, useEffect } from "react"; export default function Form() { const [hday, setHday] = useState(null); const [date, setDate] = useState(null); const [hdata, setHdata] = useState([]); const [hid, setHid] = useState(""); const [client, setClient] = useState([]); const [clientvalue, setClientValue] = useState(null); const [clientId, setClientId] = useState(""); const [cemail, setCemail] = useState([]); const [clientname, setClientname] = useState(null); const [clientEmail, setClientEmail] = useState([]); const [message, setMessage] = useState(""); const [fromField, setFromField] = useState(""); const [loading, setLoading] = useState(false); const handleRequest = async (e) => { e.preventDefault(); if (clientvalue && hday && fromField && cemail.length && clientEmail.length && message !== "") { setLoading(true); const body = { clientvalue, hday, fromField, cemail, clientEmail, message, }; await Axios.post("http://localhost:3002/mail", body, { headers: { "Content-type": "application/json", }, }) .then((res) => { alert("Email Sent Successfully"); setLoading(false); window.location.reload(); }) .catch((err) => { console.log(err); setLoading(false); }); } else { alert("Please fill all required filed"); } }; const fromFieldChange = (obj) => { setFromField(obj.target.value); }; const handleQuillChange = (e) => { setMessage(e.target.value); }; function handleHolidayChange(obj) { console.log("This is object country", obj); setHday(obj); setHid(obj._id); setDate(null); } const onChangeClient = (obj) => { console.log("client object", obj); setClientValue(obj); setClientId(obj._id); setClientname(obj.client_name); }; // 组件挂载时一次性拉取基础数据 useEffect(() => { async function getHoliday() { const url = `http://localhost:3002/api/holidays`; await Axios.get(url) .then((response) => { setHdata(response.data); }) .catch((err) => { console.log(err); }); } getHoliday(); async function getClient() { const url = `http://localhost:3002/api/client`; await Axios.get(url) .then((response) => { setClient(response.data); }) .catch((err) => { console.log(err); }); } getClient(); async function getClientEmail() { const url = `http://localhost:3002/api/emailtemplates`; await Axios.get(url) .then((response) => { setClientEmail(response.data.map((e) => e.subject)); }) .catch((err) => { console.log(err); }); } getClientEmail(); }, []); // 选中客户端变更时拉取对应邮箱 useEffect(() => { if (!clientId || !clientname) return; async function emailClick() { const url = `http://localhost:3002/api/getClientEmail/${clientId}/${clientname}`; Axios.get(url) .then((response) => { const temp = response.data.map((e) => e.email); setCemail(temp); }) .catch((error) => { console.log(error); }); } emailClick(); }, [clientId, clientname]); // 选中假期变更时拉取对应日期 useEffect(() => { if (!hid) return; async function changeDate() { const url = `http://localhost:3002/api/holiday_date/${hid}`; await Axios.get(url) .then((response) => { setDate(response.data.date_of_holiday); }) .catch((error) => { console.log(error); }); } changeDate(); }, [hid]); return ( <form onSubmit={handleRequest} method="post"> <div className="form"> <div className="form__wrapper"> <div className="form__title"> <h4>Holiday</h4> </div> <div className="form__container"> <div className="form__containerItems"> <div className="form__containerItem"> <div className="form__containerItemName"> <label>Client</label> </div> <div className="form__containerItemField"> <Select onChange={onChangeClient} placeholder="Select Client" value={clientvalue} options={client} getOptionLabel={(x) => x.client_name} getOptionValue={(x) => x._id} /> </div> </div> <div className="form__containerItem"> <div className="form__containerItemName"> <label>Holiday</label> </div> <div className="form__containerItemField"> <Select onChange={handleHolidayChange} placeholder="Select Holiday" value={hday} options={hdata} getOptionLabel={(x) => x.holiday_name} getOptionValue={(x) => x._id} /> </div> </div> <div className="form__containerItem"> <div className="form__containerItemName"> <label>Date</label> </div> <div className="form__containerItemField"> <input type="text" placeholder="date..." value={date || ''} readOnly /> </div> </div> </div> </div> <div className="form__container"> <div className="form__containerItems"> <div className="form__containerItem"> <div className="form__containerItemName"> <label>From</label> </div> <div className="form__containerItemField"> <input type="email" placeholder="Sender Email.." value={fromField} onChange={fromFieldChange} /> </div> </div> <div className="form__containerItem"> <div className="form__containerItemName"> <label>To</label> </div> <div className="form__containerItemField"> <input type="text" placeholder="Client Email.." value={cemail.join(',') || ''} readOnly /> </div> </div> <div className="form__containerItem"> <div className="form__containerItemName"> <label>Subject</label> </div> <div className="form__containerItemField"> <input type="text" placeholder="Write your Sub..." defaultValue={clientEmail} /> </div> </div> <div className="form__containerItem"> <div className="form__containerItemName"> <label>Compose Mail</label> <button disabled={loading} type="submit" className="btn btn-info" > Send </button> </div> </div> <div className="form__containerItem"> <div className="container__composeMail"> <textarea value={message} onChange={handleQuillChange} className="quill" placeholder="Enter Content from here..." /> </div> </div> </div> </div> </div> </div> </form> ); }
内容的提问来源于stack exchange,提问作者Achyuta Nanta Panda
相关产品推荐
相关产品推荐

