我正在制作我的第一个 React 应用程序,一个待办事项列表。一切看起来都很好,我遇到的唯一问题是 delete 和 complete 按钮仅在刷新页面后才起作用。好吧,实际上它们工作得很好并且应该如此(我的意思是,单击按钮后我可以在控制台中看到console.log结果),但前端结果我只能在刷新我的应用程序后才能看到。我真的不知道发生了什么事。这是代码(它很长,但我不知道问题出在哪里,所以我展示了所有代码):
import React from "react";
import "./App.css";
import axios from "axios";
import { Trash, Check } from "@phosphor-icons/react";
import { useState, useEffect } from "react";
axios.defaults.baseURL = "http://localhost:3000";
function App() {
const [activeTab, setActiveTab] = useState('ToDoTab');
const [inputName, setInputName] = useState("");
const [inputDescription, setInputDescription] = useState("");
const [tasks, setTasks] = useState([]);
const [completedTasks, setCompletedTasks] = useState([]);
useEffect(() => {
const resp = axios.get("/todos").then((resp) => {
setTasks(resp.data);
});
}, []);
useEffect(() => {
const completedResp = axios.get("/completedTodos").then((completedResp) => {
setCompletedTasks(completedResp.data);
});
}, []);
const handleTabClick = (tabId) => {
setActiveTab(tabId);
};
async function addTask(e) {
const data = {
name: inputName,
desc: inputDescription,
status: "false",
}
const resp = await axios.post("/todos", data);
if (resp.data.success) {
setTasks((prevTasks) => [...prevTasks, resp.data.newTask.rows[0]]);
}
setInputName("");
setInputDescription("");
};
async function completeTask(e, todo) {
const resp = await axios.put(`/todos/${todo.taskID}`);
if (resp.data.success) {
setTasks(prevTasks => prevTasks.filter(t => t.taskID !== todo.taskID));
}
};
async function deleteTask(e, todo) {
const resp = await axios.delete(`/todos/${todo.taskID}`);
if (resp.data.success) {
setTasks(prevTasks => prevTasks.filter(t => t.taskID !== todo.taskID));
}
};
return (
<div className="background">
<header className="todos-header">
<p className="header-text">My Todos</p>
<div className="box">
{/* Form */}
<form onSubmit={e => e.preventDefault()}>
<table className="table-one">
<tbody>
<tr>
<th><label htmlFor="title">Title:</label></th>
<th><label htmlFor="info">Description:</label></th>
</tr>
<tr>
<td><input
type="text"
id="title"
name="title"
placeholder={inputName ? "" : "What's the title of your To Do?"}
value={inputName}
onChange={(e) => setInputName(e.target.value)}></input></td>
<td><input
type="text"
id="info"
name="info"
placeholder={inputDescription ? "" : "What's the title of your To Do?"}
value={inputDescription}
onChange={(e) => setInputDescription(e.target.value)}></input></td>
<td><button
type="button"
className="add-button"
onClick={addTask}>Add</button></td>
</tr>
</tbody>
</table>
</form>
<div className="tasks">
<button
onClick={() => handleTabClick('ToDoTab')}
style={{ backgroundColor: activeTab === 'ToDoTab' ? '#02e77e' : '#5c5c5c' }}>To Do</button>
<button
onClick={() => handleTabClick('CompletedTab')}
style={{ backgroundColor: activeTab === 'CompletedTab' ? '#02e77e' : '#5c5c5c' }}>Completed</button>
{/* To Do boxes */}
<div className="tasks-holder" id="ToDoTab" style={{ display: activeTab === 'ToDoTab' ? 'block' : 'none' }}>
{tasks.map((todo, index) => {
return (
<div className="tasks-box" key={todo.taskID}>
<table className="table-two">
<tbody>
<tr>
<td>
<h1>{todo.name}</h1>
<p>{todo.description}</p>
</td>
<td>
<Trash className="icon-space trash" size={40} color="#fff" onClick={(e) => deleteTask(e, todo)} />
<Check className="icon-space check" size={40} color="#02e77e" onClick={(e) => completeTask(e, todo)} />
</td>
</tr>
</tbody>
</table>
</div>
);
})}
</div>
{/* Completed boxes */}
<div className="tasks-holder" id="CompletedTab" style={{ display: activeTab === 'CompletedTab' ? 'block' : 'none' }}>
{completedTasks.map((todo, index) => {
return (
<div className="tasks-box" key={todo.taskID}>
<table className="table-two">
<tbody>
<tr>
<td>
<h1>{todo.name}</h1>
<p>{todo.description}</p>
</td>
<td>
<Trash className="icon-space trash" size={40} color="#fff" style={{marginLeft: '20px'}} onClick={(e) => deleteTask(e, todo)} />
</td>
</tr>
</tbody>
</table>
</div>
);
})}
</div>
</div>
</div>
</header>
</div>
);
}
export default App;
后端一切似乎都很好。我已经检查过服务器上是否有任何问题,但它并没有延迟任何事情,我认为它应该正常工作。我也尝试过触发重新渲染,但效果不佳,并且控制台中没有错误。
您遇到的问题可能是由于当您删除或完成任务时状态更改后组件未重新渲染。状态更新,但 React 没有意识到它需要重新渲染组件 我更新了你的代码:
import React from "react";
import "./App.css";
import axios from "axios";
import { Trash, Check } from "@phosphor-icons/react";
import { useState, useEffect } from "react";
axios.defaults.baseURL = "http://localhost:3000";
function App() {
const [activeTab, setActiveTab] = useState('ToDoTab');
const [inputName, setInputName] = useState("");
const [inputDescription, setInputDescription] = useState("");
const [tasks, setTasks] = useState([]);
const [completedTasks, setCompletedTasks] = useState([]);
useEffect(() => {
const fetchTasks = async () => {
const resp = await axios.get("/todos");
setTasks(resp.data);
};
fetchTasks();
}, []);
useEffect(() => {
const fetchCompletedTasks = async () => {
const completedResp = await axios.get("/completedTodos");
setCompletedTasks(completedResp.data);
};
fetchCompletedTasks();
}, []);
const handleTabClick = (tabId) => {
setActiveTab(tabId);
};
const addTask = async (e) => {
const data = {
name: inputName,
desc: inputDescription,
status: "false",
};
const resp = await axios.post("/todos", data);
if (resp.data.success) {
setTasks((prevTasks) => [...prevTasks, resp.data.newTask.rows[0]]);
}
setInputName("");
setInputDescription("");
};
const completeTask = async (e, todo) => {
const resp = await axios.put(`/todos/${todo.taskID}`);
if (resp.data.success) {
setTasks(prevTasks => prevTasks.filter(t => t.taskID !== todo.taskID));
setCompletedTasks((prevCompletedTasks) => [...prevCompletedTasks, resp.data.updatedTask]);
}
};
const deleteTask = async (e, todo) => {
const resp = await axios.delete(`/todos/${todo.taskID}`);
if (resp.data.success) {
setTasks(prevTasks => prevTasks.filter(t => t.taskID !== todo.taskID));
setCompletedTasks(prevCompletedTasks => prevCompletedTasks.filter(t => t.taskID !== todo.taskID));
}
};
return (
<div className="background">
<header className="todos-header">
<p className="header-text">My Todos</p>
<div className="box">
{/* Form */}
<form onSubmit={e => e.preventDefault()}>
<table className="table-one">
<tbody>
<tr>
<th><label htmlFor="title">Title:</label></th>
<th><label htmlFor="info">Description:</label></th>
</tr>
<tr>
<td><input
type="text"
id="title"
name="title"
placeholder={inputName ? "" : "What's the title of your To Do?"}
value={inputName}
onChange={(e) => setInputName(e.target.value)}></input></td>
<td><input
type="text"
id="info"
name="info"
placeholder={inputDescription ? "" : "What's the title of your To Do?"}
value={inputDescription}
onChange={(e) => setInputDescription(e.target.value)}></input></td>
<td><button
type="button"
className="add-button"
onClick={addTask}>Add</button></td>
</tr>
</tbody>
</table>
</form>
<div className="tasks">
<button
onClick={() => handleTabClick('ToDoTab')}
style={{ backgroundColor: activeTab === 'ToDoTab' ? '#02e77e' : '#5c5c5c' }}>To Do</button>
<button
onClick={() => handleTabClick('CompletedTab')}
style={{ backgroundColor: activeTab === 'CompletedTab' ? '#02e77e' : '#5c5c5c' }}>Completed</button>
{/* To Do boxes */}
<div className="tasks-holder" id="ToDoTab" style={{ display: activeTab === 'ToDoTab' ? 'block' : 'none' }}>
{tasks.map((todo) => {
return (
<div className="tasks-box" key={todo.taskID}>
<table className="table-two">
<tbody>
<tr>
<td>
<h1>{todo.name}</h1>
<p>{todo.description}</p>
</td>
<td>
<Trash className="icon-space trash" size={40} color="#fff" onClick={(e) => deleteTask(e, todo)} />
<Check className="icon-space check" size={40} color="#02e77e" onClick={(e) => completeTask(e, todo)} />
</td>
</tr>
</tbody>
</table>
</div>
);
})}
</div>
{/* Completed boxes */}
<div className="tasks-holder" id="CompletedTab" style={{ display: activeTab === 'CompletedTab' ? 'block' : 'none' }}>
{completedTasks.map((todo) => {
return (
<div className="tasks-box" key={todo.taskID}>
<table className="table-two">
<tbody>
<tr>
<td>
<h1>{todo.name}</h1>
<p>{todo.description}</p>
</td>
<td>
<Trash className="icon-space trash" size={40} color="#fff" style={{ marginLeft: '20px' }} onClick={(e) => deleteTask(e, todo)} />
</td>
</tr>
</tbody>
</table>
</div>
);
})}
</div>
</div>
</div>
</header>
</div>
);
}
export default App;