下面是我的组件页面之一的代码。它将一些票证数据输入到 MongoDB 数据库中。我试图通过在下拉框中选择客户名称来自动填充客户信息。我在第 18 行放置了一个 console.log() 只是为了看看我是否可以弄清楚客户集合中的数据是在什么时候设置的。当我在第 18 行使用 console.log() 时,它就会出现,它运行了大约 3 次。前两次记录的是空数组,但第三次记录的是来自 MongoDB 的数据。我对编码还很陌生,我有点困惑为什么 console.log 运行了 3 次。我对发布我的代码有点害羞,因为我仍然很清楚我肯定我做错了各种事情,但是有人可以引导我朝正确的方向前进吗?它应该运行多次吗?另外,我收到以下警告,我无法弄清楚:
警告:列表中的每个子项都应该有一个唯一的“key”道具。
Check the render method of `Tickets`. See https://reactjs.org/docs/lists-and-keys.html#keys for more information.
in Fragment (created by Tickets)
in Tickets (at App.js:22)
in Route (at App.js:21)
in Switch (at App.js:20)
in div (at App.js:18)
in Router (created by BrowserRouter)
in BrowserRouter (at App.js:17)
in App (at src/index.js:6)
我知道这与我在第 97 行的下拉选择有关,但我似乎不知道如何让它快乐。
import React, { useState, useEffect } from "react";
import DeleteBtn from "../DeleteBtn";
import Jumbotron from "../Jumbotron";
import API from "../../utils/API";
import { Link } from "react-router-dom";
import { Col, Row, Container } from "../Grid";
import { List, ListItem } from "../List";
import { Input, FormBtn } from "../Form";
const moment = require('moment');
function Tickets(props) {
// Setting our component's initial state
const [tickets, setTickets] = useState([])
const [formObject, setFormObject] = useState({})
const [customers, setCustomers] = useState([])
console.log("Customer = ", customers)
// Load all tickets and store them with setTickets
useEffect(() => {
loadTickets()
}, [])
// Loads all tickets and sets them to tickets
function loadTickets() {
API.getTickets()
.then(res =>
setTickets(res.data)
)
.catch(err => console.log(err));
};
// Load all Customers and store them with setCustomer
useEffect(() => {
loadCustomers()
}, [])
// Loads all customers and sets them to customers
function loadCustomers() {
API.getCustomers()
.then(res =>
setCustomers(res.data)
)
.catch(err => console.log(err));
};
// Deletes a ticket from the database with a given id, then reloads tickets from the db
function deleteTicket(id) {
API.deleteTicket(id)
.then(res => loadTickets())
.catch(err => console.log(err));
}
// Handles updating component state when the user types into the input field
function handleInputChange(event) {
const { name, value } = event.target;
setFormObject({...formObject, [name]: value})
};
// When the form is submitted, use the API.saveTicket method to save the ticket data
// Then reload tickets from the database
function handleFormSubmit(event) {
event.preventDefault();
if (formObject.ticketDate) {
API.saveTicket({
ticketDate: formObject.ticketDate,
ticketNum: formObject.ticketNum,
ticketCust: formObject.ticketCust,
ticketCustStreet: formObject.ticketCustStreet
})
.then(res => loadTickets())
.catch(err => console.log(err));
document.getElementById("ticketFrm").reset();
setFormObject({})
}
};
return (
<Container fluid>
<Row>
<Col size="md-6">
<Jumbotron>
<h1>Add Ticket</h1>
</Jumbotron>
<form id="ticketFrm">
<Input
onChange={handleInputChange}
name="ticketDate"
placeholder="Date"
/>
<Input
onChange={handleInputChange}
name="ticketNum"
placeholder="Ticket Number (required)"
/>
<select onChange={handleInputChange}
name="ticketCust"
placeholder= "Customer Name"
style={{width: '100%', height: 35, marginBottom: 15}}>
{customers.map(customers => (
<>
<option value="" hidden>Select Customer</option>
<option default="Customer" key={customers._id}>{customers.custName}</option>
</>
))}
</select>
<Input
onChange={handleInputChange}
name="ticketCustStreet"
placeholder="Street"
/>
<Input
onChange={handleInputChange}
name="ticketCustCity"
placeholder="City"
/>
<Input
onChange={handleInputChange}
name="ticketCustState"
placeholder="State"
/>
<Input
onChange={handleInputChange}
name="ticketCustZip"
placeholder="Zip"
/>
<Input
onChange={handleInputChange}
name="ticketCustMaterial"
placeholder="Material"
/>
<FormBtn
disabled={!(formObject.ticketNum)}
onClick={handleFormSubmit}>
Submit Ticket
</FormBtn>
</form>
</Col>
<Col size="md-6 sm-12">
<Jumbotron>
<h1>Current Tickets</h1>
</Jumbotron>
{tickets.length ? (
<List>
{tickets.map(tickets => (
<ListItem key={tickets._id}>
<Link to={"/Tickets/" + tickets._id}>
<strong>
Ticket Date - {moment(tickets.ticketDate).format("MM-DD-YYYY")}
<br></br>
Ticket# - {tickets.ticketNum}
<br></br>
{tickets.ticketCust}
</strong>
</Link>
<DeleteBtn onClick={() => deleteTicket(tickets._id)}/>
</ListItem>
))}
</List>
) : (
<h3>No Results to Display</h3>
)}
</Col>
</Row>
</Container>
);
}
export default Tickets;
感谢您的任何意见,请不要对我的代码做出过分的评判。
谢谢,
-N8
首先,对某件事感到陌生是可以的,我认为你不应该道歉。 最好的学习方法是提出问题、犯错误并让你的双手“脏”,所以随意:)。
关于键问题,基本上从数组中渲染的每个元素都需要有一个唯一的键,以便 React 知道特定元素何时发生更改。 因此,您的某些数据有可能在某种程度上保持相同的
_id
。
例如,您可以在迭代中传递当前元素的索引(只需将索引参数添加到映射函数),但我不会在您的情况下使用它,因为您的列表发生变化,并且这是一种反模式。
有关密钥的更多信息,请阅读下一篇:
反应键
索引作为键反模式
关于多次出现的console.log: 没关系,因为每次组件状态发生变化时
render
都会运行。
因此,一开始您从空数组开始,当您的任何状态发生更改时,该函数将运行并打印控制台日志。
我发现了一个关于反应渲染的很好的指南,你可以深入研究:
React 渲染行为完整指南
希望您会发现它有用!
您的
useEffect
钩子在组件挂载到 DOM 后被调用。第一个控制台日志正在使用未定义的数据进行调用(因为尚未进行调用)
关于
Warning: Each child in a list should have a unique "key" prop.
。键帮助 React 识别哪些项目已更改、添加或删除。应为数组内的元素提供键,以便为元素提供稳定的标识。选择键的最佳方法是使用一个字符串来唯一标识其同级列表项。大多数情况下,您会使用数据中的 ID 作为键。当您没有稳定的渲染项目 ID 时,您可以使用项目索引作为键作为最后的手段。
https://reactjs.org/docs/lists-and-keys.html
所以为了消除你的警告,我会做类似的事情
{customers.map((customers, ii) => (
<>
<option value="" key={ii} hidden>Select Customer</option>
<option default="Customer" key={customers._id}>{customers.custName}</option>
</>
))}