列表中新添加的项目在硬刷新之前不会获得已添加的_id密钥道具



我正在尝试使用Nextjs和mongodb制作一个todo列表。

我可以将项目添加到我的列表中,它们会立即呈现,但在刷新页面之前,mongodb中使用todo发送的唯一id不会被应用,并且我收到警告:

next-dev.js?3515:25 Warning: Each child in a list should have a unique "key" prop.
Check the render method of `HomePage`. See https://reactjs.org/link/warning-keys for more information.
at Todo (webpack-internal:///./components/todo.js:10:17)
at HomePage (webpack-internal:///./pages/index.js:28:21)
at MyApp (webpack-internal:///./pages/_app.js:15:24)
at ErrorBoundary (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/client.js:8:20638)
at ReactDevOverlay (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/client.js:8:23179)
at Container (webpack-internal:///./node_modules/next/dist/client/index.js:241:5)
at AppContainer (webpack-internal:///./node_modules/next/dist/client/index.js:830:24)
at Root (webpack-internal:///./node_modules/next/dist/client/index.js:983:26)

我怀疑这是一个异步问题,但在自己和网上尝试解决后,我希望有人能解释为什么会发生这种情况。

这是我的后端代码:

import {
getAllTodos,
insertTodo,
connectDatabase,
} from "../../helpers/db-util";
async function handler(req, res) {
let client;
try {
client = await connectDatabase();
} catch (error) {
res
.status(500)
.json({ message: error.message || "Error connecting to MongoDB." });
return;
}
if (req.method === "GET") {
try {
const todosList = await getAllTodos("todos");
res.status(200).json({ todos: todosList });
} catch (error) {
res.status(500).json({
message: error.message || "Unable to fetch todos from database.",
});
}
}
if (req.method === "POST") {
const { text } = req.body;
if (!text || text.trim() === "") {
res
.status(422)
.json({ message: "You must not have a todo with no text." });
client.close();
return;
}
const newTodo = { text };
let result;
let result2;
try {
result = await insertTodo(client, "todos", newTodo);
result2 = await getAllTodos("todos");
res.status(201).json({
message: "Todo successfully added!",
todo: newTodo,
todos: result2,
});
// console.log(result, result2);
} catch (error) {
res.status(500).json({ message: error.message || "Unable to add todo." });
}
}
client.close();
}
export default handler;

这是他们使用的辅助功能的代码:

import { MongoClient } from "mongodb";
const connectionString = `mongodb+srv://${process.env.mongodb_username}:${process.env.DB_PASS}@${process.env.mongodb_clustername}.e79y2.mongodb.net/${process.env.mongodb_db_name}?retryWrites=true&w=majority`;
export async function connectDatabase() {
const client = await MongoClient.connect(connectionString);
return client;
}
export async function insertTodo(client, collection, todo) {
const db = client.db();
const result = await db.collection(collection).insertOne(todo);
return result;
}
export async function getAllTodos(collection) {
const client = await connectDatabase();
const db = client.db();
const todos = await db.collection(collection).find().toArray();
return todos;
}

在前端,我最初用getServerSideProps加载todo,所有这些todo都正确应用了_id键,但当我添加一个新的todo时,我会收到键警告,即使在新创建的项的console.log中会将_id显示为todo对象的一部分,它也不会应用于映射的todo列表。

export default function HomePage(props) {
const { todos } = props;
const [todosList, setTodosList] = useState([]);
const [isLoading, setIsLoading] = useState(false);
function getTodos() {
const parsedTodos = JSON.parse(todos);
setTodosList(parsedTodos);
}
useEffect(() => {
getTodos();
}, [todos]);
return (
<div>
<Head>
<title>Next Todos</title>
<meta name="description" content="NextJS todos app" />
</Head>
<main>
<TodoInput
getTodos={getTodos}
setTodosList={setTodosList}
todosList={todosList}
setIsLoading={setIsLoading}
/>
{!isLoading &&
todosList.map((todo) => (
<Todo key={todo._id} id={todo._id} text={todo.text} />
))}
</main>
</div>
);
}
export async function getServerSideProps(context) {
let client;
client = await connectDatabase();
// console.log(client);
const todosList = await getAllTodos("todos");
const allTodos = JSON.stringify(todosList);
return {
props: {
todos: allTodos,
},
};
}

这是Todos输入表单和提交处理程序:

const TodoInput = (props) => {
async function postNewTodo(enteredTodo) {
await fetch("/api/todos", {
method: "POST",
body: JSON.stringify({ text: enteredTodo }),
headers: { "Content-Type": "application/json" },
})
.then((response) => response.json())
.then((data) => {
props.setIsLoading(true);
console.log(data.todos);
props.setTodosList([
...props.todosList,
{ id: data.todo._id, text: data.todo.text },
]);
props.setIsLoading(false);
});
}
const todoInputRef = useRef();
const handleSubmit = (e) => {
e.preventDefault();
const enteredTodo = todoInputRef.current.value;
postNewTodo(enteredTodo);
// props.setTodosList([...props.todosList, { text: enteredTodo }]);
};
return (
<Fragment>
<form onSubmit={handleSubmit}>
<input type="text" required ref={todoInputRef} />
<button>Add Todo</button>
</form>
</Fragment>
);
};
export default TodoInput;

我试图使用加载状态来减慢新添加对象的映射速度,这样它就有时间正确地应用其密钥,但无济于事。

如果您能提供任何帮助来解释为什么会发生这种情况,我们将不胜感激。

如您的代码中所示,在添加todo时,您将id作为todo项的成员,但在呈现您正在使用的项时_id,它是未定义的,因此数组中的所有todo项都具有相同的值,_id=未定义,因此使用id而不是_id将清除类似以下的警告

<main>
<TodoInput
getTodos={getTodos}
setTodosList={setTodosList}
todosList={todosList}
setIsLoading={setIsLoading}
/>
{!isLoading &&
todosList.map((todo) => (
<Todo key={todo.id} id={todo.id} text={todo.text} />
))}
</main>

我对nextjs不太了解!这里有一个反应选项!

由于您使用的数据是以异步方式来的,请尝试使用async too 调用useEffect

async function getTodos() {
const parsedTodos = await JSON.parse(todos);
setTodosList(parsedTodos);
}

或者如果你不去密钥使用这个

<main>
<TodoInput
getTodos={getTodos}
setTodosList={setTodosList}
todosList={todosList}
setIsLoading={setIsLoading}
/>
{!isLoading &&
todosList.map((todo, i) => (
<Todo key={i} id={todo._id} text={todo.text} />
))}
</main>

其中i是该数组中的todo索引

已解决!

props.setTodosList([
...props.todosList,
{ _id: data.todo._id, text: data.todo.text },
]);

在todo-input.js中,最初标记为id而不是id。

感谢你们两个在这件事上帮助我的人。

最新更新