I am developing a User Profile page, where users update their profile info. The first time when a user fills the form it is successfully saved in the firestore database. But the next time when the user only want to update a specific info in the form and clicks save, the whole document gets updated. Only the changed field info is reflected in the database and rest all the fields become empty.
I’ve tried using updateDoc() function provided by Firebase but I am not able to update the info using user uid, so that the database would know which user changed their info.
ProfilePage.jsx
const { user } = UserAuth();
const [firstName, setFirstName] = useState("")
const [lastName, setLastName] = useState("")
const [email, setEmail] = useState("")
const [file, setFile] = useState("")
const [channelName, setChannelName] = useState("")
const [channelLink, setChannelLink] = useState("")
const [data, setData] = useState([])
const profileDB = collection(db, "UserProfiles");
const handleUpload = (e) => {
e.preventDefault();
console.log(e.target.files[0]);
const Imgs = ref(storage, `profilePictures/${uuid()}`);
uploadBytes(Imgs, e.target.files[0]).then((data) => {
console.log(data, "imgs");
getDownloadURL(data.ref).then((val) => {
setFile(val);
});
});
setFile("")
};
const handleClick = async (e) => {
e.preventDefault();
await setDoc(doc(profileDB, user.uid), {
uid: user.uid,
firstName: firstName,
lastName: lastName,
email: email,
channelName: channelName,
channelLink: channelLink,
ImgURL: file
});
alert("Data added successfully");
setFile("")
};
const handleUpdate = async () => {
const updateData = doc(db, "userProfile", user.uid);
await updateDoc(updateData, {
firstName: firstName,
lastName: lastName,
email: email,
channelLink: channelLink,
channelName: channelName,
ImgURL: file,
})
}
// get user data according to user uid
const getData = async () => {
const profileDB = doc(db, "UserProfiles", user.uid);
const profileDBSnap = await getDoc(profileDB);
setData(profileDBSnap.data())
console.log([data])
};
useEffect(() => {
getData()
},[])
return (
{
[data].map((item) => (
<>
<div className='max-w-[1280px] mx-auto flex items-center justify-between relative bottom-10'>
<div className='flex items-center gap-6'>
<div>
<img className='h-40 w-40 rounded-full' src={ item?.ImgURL || user?.photoURL || ProfilePic} alt="profile photo" />
</div>
<div>
<h1 className='text-3xl font-medium'>{ item?.firstName || user?.displayName || "User"}</h1>
<p className='text-[#667085]'>@{ item?.firstName || user?.displayName || "user"}</p>
</div>
</div>
<div className='flex items-center gap-3'>
<Link to={"/influencer/infdashboard"}>
<Button style={"border border-[#D0D5DD] bg-white py-2 px-4 rounded-lg text-sm font-semibold py-[10px]"} text={"Cancel"} />
</Link>
<Button style={"bg-[#F26522] text-white text-sm font-semibold rounded-lg px-4 py-[10px]"} text={"Save"} />
</div>
</div>
<form className='max-w-[640px] mx-auto'>
<div className='flex flex-col md:flex md:flex-row md:items-center md:justify-between gap-6 border-b border-[#EAECF0] pb-5'>
<div className='flex flex-col gap-[6px] w-full'>
<label className='text-[#344054] text-sm font-medium'>
First name
</label>
<input type="text" placeholder={ item?.firstName || 'first name'} value={firstName} onChange={(e) => setFirstName(e.target.value)} className='outline-1 border border-gray-300 outline-[#F26522] p-2 rounded-lg' />
</div>
<div className='flex flex-col gap-[6px] w-full'>
<label className='text-[#344054] text-sm font-medium'>
Last name
</label>
<input type="text" placeholder={ item?.lastName || 'last name'} value={lastName} onChange={(e) => setLastName(e.target.value)} className='outline-1 border border-gray-300 outline-[#F26522] p-2 rounded-lg' />
</div>
</div>
<div className='py-5 flex flex-col gap-[6px] border-b border-[#EAECF0]'>
<label className='text-[#344054] text-sm font-medium'>
Email
</label>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder={ item?.email || '[email protected]'} className='outline-1 border border-gray-300 outline-[#F26522] p-2 rounded-lg' />
</div>
<div className='py-5 flex items-start gap-5 border-b border-[#EAECF0]'>
<img className='h-16 w-16 rounded-full' src={ item?.ImgURL || user?.photoURL || Avatar} alt="pic" />
<div className='bg-white w-full rounded-lg border border-[#EAECF0] flex flex-col justify-center items-center px-6 py-4'>
<input type="file" onChange={(e) => handleUpload(e)} />
<img src={UploadPic} alt="upload profile pic" />
<p className='text-[#667085] text-xs pt-3 text-center'><span className='text-[#4285F4] text-sm font-semibold tracking-wide'>Click to upload profile photo</span> or drag and drop <br /> SVG, PNG, JPG or GIF (max. 800x400px) </p>
</div>
</div>
<div className='py-5 border-b border-[#EAECF0]'>
<div className='flex flex-col gap-[6px] w-full pb-5'>
<label className='text-[#344054] text-sm font-medium'>
Channel Name
</label>
<input type="text" placeholder={ item?.channelName || "Channel Name"} value={channelName} onChange={(e) => setChannelName(e.target.value)} className='outline-1 border border-gray-300 outline-[#F26522] p-2 rounded-lg' />
</div>
<div className='flex flex-col gap-[6px] w-full'>
<label className='text-[#344054] text-sm font-medium'>
Channel Link
</label>
<input type="text" placeholder={ item?.channelLink || "Channel Link"} value={channelLink} onChange={(e) => setChannelLink(e.target.value)} className='outline-1 border border-gray-300 outline-[#F26522] p-2 rounded-lg' />
</div>
</div>
<div className='flex justify-end gap-3 py-5 '>
<Link to={"/influencer/infdashboard"}>
<Button style={"border border-[#D0D5DD] bg-white py-2 px-4 rounded-lg text-sm font-semibold py-[10px] md:w-max"} text={"Cancel"} />
</Link>
<Button style={"bg-[#F26522] text-white text-sm font-semibold rounded-lg px-4 py-[10px] md:w-max"} text={"Save"} event={handleClick} />
</div>
</form>
</>
))
}
2
Answers
I found the solution. Here's how the handleUpdate() looks like:
It sounds like this code is not doing what you want:
When you call
updateDoc
, Firestore replaces the value of any field you specify in the database with the value you specify in the call. So if some of the values are empty, those fields will become empty in the database.To leave fields unmodified, you should not specify those fields in the call. For example, this will only modify
firstName
andlastName
and leave the other fields as is:If you don’t know ahead of time which fields have a value, you can build an object dynamically with this:
I left
ImgURL
/file
out in the example above, as I don’t know its type, but you will have to do a similar check for that.