React组件无限循环加载故障 如何使其仅依据数据库返回数据触发加载
问题根源
你的代码里存在两处触发无限渲染的逻辑:
- Added组件中,
useEffect的依赖项设置为data,但useEffect内部调用setData会修改data的值,导致每次请求完成后都会重新触发useEffect,形成死循环。 - App父组件中,
useEffect直接对state.list赋值(属于直接修改useReducer的状态,违反React状态不可变原则),同时该useEffect的依赖项设置为state.list,修改state.list后会重新触发useEffect,形成第二处死循环。
修复方案
1. 修复Added组件
把useEffect的依赖项改为空数组,让它只在组件首次挂载时触发请求即可;如果需要数据更新时重新请求,可以改为依赖父组件传入的更新标识:
import React from "react"; import Axios from "axios"; import { useState, useEffect } from "react"; function Added() { const [data, setData] = useState([]); // 依赖改为空数组,仅首次挂载执行 useEffect(() => { Axios.get("http://localhost:3001/").then((result) => { setData(result.data); }).catch(err => console.log(err)); }, []); console.log("1"); return data.map((item) => { return ( <div key={item._id}> <h1>{item.finame}</h1> <h1>{item.laname}</h1> <h5>{item.age}</h5> </div> ); }); } export default Added;
2. 修复App父组件
删除直接修改state.list的useEffect,调整useReducer的初始值,不要把异步函数直接放到初始state里:
import "./App.css"; import { useState, useReducer, useEffect } from "react"; import Added from "./added"; import Axios from "axios"; function App() { /* -------------------- For the useReducer -------------------- */ const Actions = { Add: "add", SetList: "setList" }; // 初始值设为空数组 const defaultState = { list: [], }; const reducer = (state, action) => { switch (action.type) { case Actions.Add: const listItem = action.payload; try { Axios.post("http://localhost:3001/add", listItem); } catch (error) { console.log(error + "444444"); } return { ...state, list: [...state.list, listItem] }; case Actions.SetList: return { ...state, list: action.payload }; default: console.log("this is the default"); return state; } }; const [state, dispatch] = useReducer(reducer, defaultState); // 首次挂载拉取列表数据,通过dispatch更新state,不要直接修改 useEffect(() => { Axios.get("http://localhost:3001/").then((result) => { dispatch({type: Actions.SetList, payload: result.data}) }).catch(err => console.log(err)); }, []); /* ---------------------------- For the form ---------------------------- */ const [listItem, setListItem] = useState({ finame: "", laname: "", age: 0 }); const handelChange = (e) => { const name = e.target.name; const value = e.target.value; setListItem({ ...listItem, [name]: value }); }; const handelSubmit = (e) => { e.preventDefault(); dispatch({ type: Actions.Add, payload: listItem }); }; /* ------------------------ for the form return ---------------------- */ return ( <div className="App"> <h1>CRUD app using MERN stack</h1> <form onSubmit={handelSubmit}> <label htmlFor="finame">First name:</label> <input type="text" name="finame" id="finame" value={listItem.finame} onChange={handelChange} /> <label htmlFor="laname">Last name:</label> <input type="text" name="laname" id="laname" value={listItem.laname} onChange={handelChange} /> <label htmlFor="age">Age:</label> <input type="Number" name="age" id="age" value={listItem.age} onChange={handelChange} /> <button type="Submit">Submit</button> </form> {state.list.length > 0 && <Added />} </div> ); } export default App;
额外优化建议
- 父子组件不要重复请求同一个接口,你可以把列表数据从App组件通过props传给Added组件,减少不必要的网络请求。
- 提交数据的post请求是异步操作,建议等待请求成功后再更新本地state,避免本地数据和服务端数据不一致。
内容的提问来源于stack exchange,提问作者zouhaier
相关产品推荐
相关产品推荐

