skip to Main Content

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


  1. Chosen as BEST ANSWER

    I found the solution. Here's how the handleUpdate() looks like:

    const handleUpdate = async (e) => {
        e.preventDefault();
    
        try {
            const profileDB = doc(db, "UserProfiles", user.uid);
            const profileDBSnap = await getDoc(profileDB);
            if (profileDBSnap.exists()) {
                // Document exists, update the fields
                const existingData = profileDBSnap.data();
    
                const updatedData = {
                    ...existingData,
                    firstName: firstName || existingData.firstName,
                    lastName: lastName || existingData.lastName,
                    email: email || existingData.email,
                    channelName: channelName || existingData.channelName,
                    channelLink: channelLink || existingData.channelLink,
                    ImgURL: file || existingData.ImgURL,
                };
    
                // Save the updated data back to Firestore
                await setDoc(profileDB, updatedData);
    
                alert("Data updated successfully");
    
                nav("/influencer/infdashboard");
            } else {
                // Document doesn't exist, handle accordingly
                alert("User profile not found. Create a new profile.");
            }
    
        } catch (err) {
            console.log(err)
        }
    }
    

  2. It sounds like this code is not doing what you want:

    await updateDoc(updateData, {
        firstName: firstName,
        lastName: lastName,
        email: email,
        channelLink: channelLink,
        channelName: channelName,
        ImgURL: file,
    })
    

    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 and lastName and leave the other fields as is:

    await updateDoc(updateData, {
        firstName: firstName,
        lastName: lastName,
    })
    

    If you don’t know ahead of time which fields have a value, you can build an object dynamically with this:

    let updates = {};
    if (firstName && firstName.length > 0) updates['firstName'] = firstName;
    if (lastName && lastName.length > 0) updates['lastName'] = lastName;
    if (email && email.length > 0) updates['email'] = email;
    if (channelLink && channelLink.length > 0) updates['channelLink'] = channelLink;
    if (channelName && channelName.length > 0) updates['channelName'] = channelName;
    
    await updateDoc(updateData, updates)
    

    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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search