英文:
List getting duplicated from Firestore Collections
问题
I can help you with the translation of your code. Here are the code portions you provided:
// 以下是添加数据到集合的代码
import Sidebar from './../../components/sidebar/Sidebar';
import Navbar from './../../components/navbar/Navbar';
import "./new.scss";
import { useState } from 'react';
import DriveFolderUploadIcon from '@mui/icons-material/DriveFolderUpload';
import { addDoc, collection, serverTimestamp } from "firebase/firestore";
import { db } from '../../firebase';
import { useNavigate } from 'react-router-dom';
const New = ({inputs}) => {
const [file, setFile] = useState("");
const [data, setData] = useState({});
const navigate = useNavigate();
const handleInput = (e) =>{
const id = e.target.id;
const value = e.target.value;
setData({...data, [id]:value});
}
const handleAdd = async(e) => {
e.preventDefault();
// 在集合 "clients" 中添加新文档
await addDoc(collection(db, "clients"), {
...data,
timeStamp: serverTimestamp(),
});
navigate("/users");
}
return (
<div className='new'>
<Sidebar/>
<div className="newContainer">
<Navbar/>
<div className="top">
<h1>Add New Client</h1>
</div>
<div className="bottom">
<div className="left">
<img src={file ? URL.createObjectURL(file): "https://icon-library.com/images/no-image-icon/no-image-icon-0.jpg"} alt=''/>
</div>
<div className="right">
<form onSubmit={handleAdd}>
<div className="formInput">
<label htmlFor="file">
Image: <DriveFolderUploadIcon className="icon" />
</label>
<input
type="file"
id="file"
onChange={(e) => setFile(e.target.files[0])}
style={{ display: "none" }}
/>
</div>
{inputs.map((input) => (
<div className="formInput" key={input.id}>
<label>{input.label}</label>
<input
id={input.id}
type={input.type}
placeholder={input.placeholder}
onChange={handleInput}
/>
</div>
))}
<button type="submit">
Send
</button>
</form>
</div>
</div>
</div>
</div>
)
}
export default New
// 以下是从集合中获取数据的代码
import { DataGrid } from "@mui/x-data-grid";
import "./datatable.scss";
import { userColumns } from "../../datatablesource";
import VisibilityIcon from '@mui/icons-material/Visibility';
import EditNoteIcon from '@mui/icons-material/EditNote';
import DeleteIcon from '@mui/icons-material/Delete';
import { Link } from "react-router-dom";
import { useEffect, useState } from "react";
import { Tooltip } from "@mui/material";
import { collection, getDocs } from "firebase/firestore";
import { doc, deleteDoc } from "firebase/firestore";
import { db } from "../../firebase";
const Datatable = () => {
const [data, setData] = useState([]);
let list = [];
useEffect(() =>{
const fetchData = async () =>{
const querySnapshot = await getDocs(collection(db, "clients"));
querySnapshot.forEach((doc) => {
list.push({ id: doc.id, ...doc.data()})
});
setData(list);
}
fetchData();
}, [])
const handleDelete = async (id) => {
try {
await deleteDoc(doc(db, "clients", id));
setData(data.filter((item) => item.id !== id));
} catch (err) {
console.log(err);
}
};
const actionColumn = [
{ field: "action", headerName: "Action", width: 150, renderCell:(params)=>{
return (
<div className="cellAction">
<Link>
<Tooltip title="View Client" placement="bottom">
<div className="viewButton"><VisibilityIcon/></div>
</Tooltip>
</Link>
<Link>
<Tooltip title="Edit Client" placement="bottom">
<div className="editButton"><EditNoteIcon/></div>
</Tooltip>
</Link>
<Link>
<Tooltip title="Delete Client" placement="bottom">
<div className="deleteButton" onClick={() => handleDelete(params.row.id)}><DeleteIcon/></div>
</Tooltip>
</Link>
</div>
)
}}
]
return (
<div className="datatable">
<div className="datatableTitle">
Add New Client
<Link to="/users/new" className="link">
Add New
</Link>
</div>
<DataGrid
className="datagrid"
rows={data}
columns={userColumns.concat(actionColumn)}
pageSize={9}
rowsPerPageOptions={[9]}
checkboxSelection
/>
</div>
)
}
export default Datatable
If you have any specific questions or need further assistance, please let me know.
英文:
currently I'm trying to get document from a collection on firestore, each time I add a document on my application, the list gets duplicated on my application yet I'm only viewing an ID for that document in the firestore collection...
Here's my code for add data to the collection:-
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
import Sidebar from './../../components/sidebar/Sidebar';
import Navbar from './../../components/navbar/Navbar';
import "./new.scss"
import { useState } from 'react';
import DriveFolderUploadIcon from '@mui/icons-material/DriveFolderUpload';
import { addDoc, collection, serverTimestamp } from "firebase/firestore";
import { db } from '../../firebase';
import { useNavigate } from 'react-router-dom';
const New = ({inputs}) => {
const [file, setFile] = useState("");
const [data, setData] = useState({});
const navigate = useNavigate();
const handleInput = (e) =>{
const id = e.target.id;
const value = e.target.value
setData({...data, [id]:value});
}
const handleAdd = async(e) => {
e.preventDefault();
// Add a new document in collection "cities"
await addDoc(collection(db, "clients"), {
...data,
timeStamp: serverTimestamp(),
});
navigate("/users")
}
return (
<div className='new'>
<Sidebar/>
<div className="newContainer">
<Navbar/>
<div className="top">
<h1>Add New Client</h1>
</div>
<div className="bottom">
<div className="left">
<img src={file ? URL.createObjectURL(file): "https://icon-library.com/images/no-image-icon/no-image-icon-0.jpg"} alt=''/>
</div>
<div className="right">
<form onSubmit={handleAdd}>
<div className="formInput">
<label htmlFor="file">
Image: <DriveFolderUploadIcon className="icon" />
</label>
<input
type="file"
id="file"
onChange={(e) => setFile(e.target.files[0])}
style={{ display: "none" }}
/>
</div>
{inputs.map((input) => (
<div className="formInput" key={input.id}>
<label>{input.label}</label>
<input
id={input.id}
type={input.type}
placeholder={input.placeholder}
onChange={handleInput}
/>
</div>
))}
<button type="submit">
Send
</button>
</form>
</div>
</div>
</div>
</div>
)
}
export default New
<!-- end snippet -->
Here's also the code where I'm fetching the data from the collection:-
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
import { DataGrid } from "@mui/x-data-grid";
import "./datatable.scss";
import { userColumns } from "../../datatablesource";
import VisibilityIcon from '@mui/icons-material/Visibility';
import EditNoteIcon from '@mui/icons-material/EditNote';
import DeleteIcon from '@mui/icons-material/Delete';
import { Link } from "react-router-dom";
import { useEffect, useState } from "react";
import { Tooltip } from "@mui/material";
import { collection, getDocs } from "firebase/firestore";
import { doc, deleteDoc } from "firebase/firestore";
import {db} from "../../firebase"
const Datatable = () => {
const [data, setData] = useState([]);
let list = []
useEffect(() =>{
const fetchData = async () =>{
const querySnapshot = await getDocs(collection(db, "clients"));
querySnapshot.forEach((doc) => {
list.push({ id: doc.id, ...doc.data()})
});
setData(list);
}
fetchData();
}, [])
const handleDelete = async (id) => {
try {
await deleteDoc(doc(db, "clients", id));
setData(data.filter((item) => item.id !== id));
} catch (err) {
console.log(err);
}
};
const actionColumn = [
{ field: "action", headerName: "Action", width: 150, renderCell:(params)=>{
return (
<div className="cellAction">
<Link>
<Tooltip title="View Client" placement="bottom">
<div className="viewButton"><VisibilityIcon/></div>
</Tooltip>
</Link>
<Link>
<Tooltip title="Edit Client" placement="bottom">
<div className="editButton"><EditNoteIcon/></div>
</Tooltip>
</Link>
<Link>
<Tooltip title="Delete Client" placement="bottom">
<div className="deleteButton" onClick={() => handleDelete(params.row.id)}><DeleteIcon/></div>
</Tooltip>
</Link>
</div>
)
}}
]
return (
<div className="datatable">
<div className="datatableTitle">
Add New Client
<Link to="/users/new" className="link">
Add New
</Link>
</div>
<DataGrid
className="datagrid"
rows={data}
columns={userColumns.concat(actionColumn)}
pageSize={9}
rowsPerPageOptions={[9]}
checkboxSelection
/>
</div>
)
}
export default Datatable
<!-- end snippet -->
Yet it's still getting duplicated on my list, can anyone assist me on this??
答案1
得分: 1
将 list
移动到 fetchData
函数内部:
let list = []; // <-- NOT HERE!
useEffect(() =>{
const fetchData = async () =>{
let list = []; // <-- MOVE HERE
const querySnapshot = await getDocs(collection(db, "clients"));
querySnapshot.forEach((doc) => {
list.push({ id: doc.id, ...doc.data()})
});
setData(list);
}
fetchData();
}, [])
在 React v18 中,组件会呈现两次(在 ReactStrict 模式下)。fetchData
外部的 list
创建了一个闭包并且两次追加(push)数据。
旁注:如果需要追加到 data
状态变量中,则使用状态设置器的回调版本:
setData( prevData => /* 使用 prevData 和 list 进行操作 */ );
英文:
Move the list
inside the fetchData function:
let list = []; // <-- NOT HERE!
useEffect(() =>{
const fetchData = async () =>{
let list = []; // <-- MOVE HERE
const querySnapshot = await getDocs(collection(db, "clients"));
querySnapshot.forEach((doc) => {
list.push({ id: doc.id, ...doc.data()})
});
setData(list);
}
fetchData();
}, [])
In React v18, the Component will render twice (in ReactStrict mode). The list
outside the fetchData
creates a closure and appends (push) the data twice.
Sidenote: If you need to append to the data
state variable, then use the callback version of the state setter:
setData( prevData => /* Do something with prevData and list */ );
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论