MERN Todo应用中React组件未按需重新渲染问题求助
MERN待办应用:新增任务后TodoList不重新渲染问题排查
问题描述
我正在开发一个基于MERN架构的待办事项应用,新增任务可以成功写入数据库,但TodoList组件不会自动重新渲染展示新任务。已经尝试用useState和useEffect触发重渲染,但没效果,且useEffect能正常获取到数据,求排查原因。
代码片段
App主文件
import React, {useState, useEffect} from 'react'; import './App.css'; import TodoHeader from './components/TodoHeader'; import TodoForm from './components/TodoForm'; import TodoList from './components/TodoList'; function App() { const [tasks, setTasks] = useState([]); useEffect(() => { const fetchData = async () => { try { const response = await fetch('http://localhost:8080/fetch_records'); const data = await response.json(); setTasks(data); console.log("Fetched data", data) } catch (error) { console.error('Error fetching tasks:', error); } }; fetchData(); }, []); return ( <div className="App"> <TodoHeader /> <TodoForm /> <TodoList item={{tasks}}/> </div> ); } export default App;
TodoList组件
import React, {useState, useEffect} from 'react' import './TodoList.css' export default function TodoList({item}) { const [tasks, setTasks] = useState([]) useEffect(() => { const fetchData = async () => { const response = await fetch('http://localhost:8080/fetch_records') let data = await response.json() setTasks((prevTasks) => [...prevTasks, ...data]); //setTasks(data) } fetchData() }, []) return ( <div> {/* Display the list of tasks */} <ul> {item.tasks.map((task) => ( <li key={task._id}>{task.task} — {task.dueDate}</li> ))} </ul> </div> ) }
TodoForm组件
import React, { useState } from 'react' import './TodoForm.css' import TodoList from './TodoList'; export default function TodoForm() { const [tasks, setTasks] = useState([]); const handleSubmit = async (event) => { event.preventDefault(); // Prevent the default form submission behavior const response = await fetch('http://localhost:8080/todo-submit', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ task: event.target.task.value, // Access form input values using event.target dueDate: event.target.dueDate.value, isAccomplished: false }), }); const selector = document.querySelector('.result'); try { const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { const result = await response.json(); // Now you can use the 'result' object to dynamically update your UI if (result.success) { selector.style.color = "green" selector.insertAdjacentHTML("afterbegin", "<b>" + result.message + "</b>") //console.log('Success:', result.message); // Update your UI here, e.g., show a success message in a div const tasksResponse = await fetch('http://localhost:8080/fetch_records'); // Replace with your actual API endpoint const tasksData = await tasksResponse.json(); setTasks(tasksData); } else { selector.style.color = "red" selector.insertAdjacentHTML("afterbegin", "<b>" + result.errorMessage + "</b>") //console.error('Error:', result.errorMessage); // Handle the error, e.g., show an error message in a div } } else { const nonJsonResponse = await response.text(); selector.style.color = "red" selector.insertAdjacentHTML("afterbegin", "<u>" + nonJsonResponse + "</u>") } } catch (error) { selector.style.color = "red" selector.insertAdjacentHTML("afterbegin", "<b><u>" + error + "</u></b>") //console.error('Error:', error); // Handle unexpected errors, e.g., show a generic error message in a div } }; return( <> <form action="http://localhost:8080/todo-submit" method="post" onSubmit={handleSubmit}> <div class="input-group"> <span class="input-group-text">Enter your task to be done</span> <textarea class="form-control" aria-label="With textarea" name="task" /> </div> <div class="input-group"> <span class="input-group-text">Choose the due date for this task: </span> <input type="date" class="form-control" name="dueDate" /> </div> <div class="form-group"> <button type="submit" class="btn btn-primary">Submit data</button> </div> </form> <div className="result form-control"></div> </> ) }
问题原因分析
- 状态分散管理:App、TodoList、TodoForm各自维护独立的
tasks状态,TodoForm提交后更新的是自身内部的tasks,与App中的tasks无关联,导致App状态不刷新,TodoList接收的props也不会变化。 - TodoList冗余逻辑:TodoList内部定义了
tasks状态和挂载时的fetch逻辑,但渲染时实际使用的是App传递的item.tasks,内部状态完全未被利用;同时useEffect仅在组件挂载时执行一次,后续不会重新触发。 - 直接操作DOM:TodoForm中通过
document.querySelector直接操作DOM显示提示,不符合React状态管理模式,代码耦合度高。
修复方案
1. 统一状态到App组件
所有任务列表相关状态由App统一管理,子组件通过props接收状态和更新回调,实现单向数据流。
修改后的App组件
import React, {useState, useEffect} from 'react'; import './App.css'; import TodoHeader from './components/TodoHeader'; import TodoForm from './components/TodoForm'; import TodoList from './components/TodoList'; function App() { const [tasks, setTasks] = useState([]); // 封装获取任务的逻辑,方便复用 const fetchTasks = async () => { try { const response = await fetch('http://localhost:8080/fetch_records'); const data = await response.json(); setTasks(data); console.log("Fetched data", data) } catch (error) { console.error('Error fetching tasks:', error); } }; useEffect(() => { fetchTasks(); }, []); return ( <div className="App"> <TodoHeader /> {/* 传递fetchTasks给TodoForm,提交成功后触发更新 */} <TodoForm onTaskAdded={fetchTasks} /> {/* 直接传递tasks,简化props */} <TodoList tasks={tasks}/> </div> ); } export default App;
修改后的TodoList组件
移除内部冗余状态和fetch逻辑,直接使用props传递的tasks,props变化时组件自动重新渲染:
import React from 'react' import './TodoList.css' export default function TodoList({tasks}) { return ( <div> <ul> {tasks.map((task) => ( <li key={task._id}>{task.task} — {task.dueDate}</li> ))} </ul> </div> ) }
修改后的TodoForm组件
移除内部tasks状态,用React状态管理提示信息,避免直接操作DOM,提交成功后调用App传递的onTaskAdded回调:
import React, { useState } from 'react' import './TodoForm.css' export default function TodoForm({onTaskAdded}) { const [message, setMessage] = useState(''); const [messageStyle, setMessageStyle] = useState({}); const handleSubmit = async (event) => { event.preventDefault(); setMessage(''); // 清空之前的提示 const response = await fetch('http://localhost:8080/todo-submit', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ task: event.target.task.value, dueDate: event.target.dueDate.value, isAccomplished: false }), }); try { const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { const result = await response.json(); if (result.success) { setMessage(`<b>${result.message}</b>`); setMessageStyle({color: 'green'}); // 调用回调通知App更新任务列表 await onTaskAdded(); // 清空表单 event.target.reset(); } else { setMessage(`<b>${result.errorMessage}</b>`); setMessageStyle({color: 'red'}); } } else { const nonJsonResponse = await response.text(); setMessage(`<u>${nonJsonResponse}</u>`); setMessageStyle({color: 'red'}); } } catch (error) { setMessage(`<b><u>${error}</u></b>`); setMessageStyle({color: 'red'}); } }; return( <> <form onSubmit={handleSubmit}> <div className="input-group"> <span className="input-group-text">Enter your task to be done</span> <textarea className="form-control" aria-label="With textarea" name="task" /> </div> <div className="input-group"> <span className="input-group-text">Choose the due date for this task: </span> <input type="date" className="form-control" name="dueDate" /> </div> <div className="form-group"> <button type="submit" className="btn btn-primary">Submit data</button> </div> </form> <div className="result form-control" style={messageStyle} dangerouslySetInnerHTML={{__html: message}}></div> </> ) }
修复说明
- 状态统一由App管理,TodoForm提交成功后触发App的
fetchTasks更新tasks状态,TodoList接收的props变化后自动重新渲染。 - 移除TodoList内部冗余的状态和fetch逻辑,简化组件职责。
- 用React状态管理提示信息,替代直接操作DOM,符合React开发模式。
内容的提问来源于stack exchange,提问作者m.kreyn
相关产品推荐
相关产品推荐

