OK let me show the whole picture of this problem. Firstly, I think 5 pages involve in this situation > 1. user.js in backend (logic) 2. userSlice.jsx in redux folder 3. store.jsx in redux folder 4. NewAccountPage 5. ProfileEditPage. Now when I signIn (I use firebase auth) the page will show at NewAccountPage so I click create button then It relocated to ProfileEditPage which brought all of data (username, date, about, profilePicture) to show up in ProfileEditPage perfectly and data saved in database but when I want to update some data it shows PATCH http://localhost:5173/api/users/update/undefined 500 (Internal Server Error) which currentUser._id is undefined when trying to make the API call to update the user profile. let see the code below >>
NewAccountPage >
import React, { useEffect, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import {
getDownloadURL,
getStorage,
ref,
uploadBytesResumable,
} from 'firebase/storage';
import { firebase } from '../Firebase';
//redux
import {
createStart,
createSuccess,
createFailure,
updateCurrentUser
} from '../redux/userSlice';
import { useSelector } from 'react-redux';
import { useDispatch } from 'react-redux';
import UserAvatar from '../Chat/UserAvatar';
// css
import { BackgroundProfileContainer,
StyledInput, DivImage, SaveBtnStyle} from "./NewAccountElements";
import { CircularProgressbar } from 'react-circular-progressbar';
import { Alert } from 'flowbite-react';
function NewAccountPage() {
const { currentUser, error, loading } = useSelector((state) => state.user) || {};
const [imageFile, setImageFile] = useState(null);
const [imageFileUrl, setImageFileUrl] = useState(null);
const [imageFileUploadProgress, setImageFileUploadProgress] = useState(null);
const [imageFileUploadError, setImageFileUploadError] = useState(null);
const [imageFileUploading, setImageFileUploading] = useState(false);
const [createUserSuccess, setCreateUserSuccess] = useState(null);
const [createUserError, setCreateUserError] = useState(null);
const [formData, setFormData] = useState({
username: '',
date: '',
about: '',
profilePicture: '',
});
const filePickerRef = useRef();
const dispatch = useDispatch();
const navigate = useNavigate();
const handleImageChange = (e) => {
const file = e.target.files[0];
if (file) {
setImageFile(file);
setImageFileUrl(URL.createObjectURL(file));
}
};
useEffect(() => {
if (imageFile) {
uploadImage();
}
}, [imageFile]);
const uploadImage = async () => {
// service firebase.storage {
// match /b/{bucket}/o {
// match /{allPaths=**} {
// allow read;
// allow write: if
// request.resource.size < 2 * 1024 * 1024 &&
// request.resource.contentType.matches('image/.*')
// }
// }
// }
setImageFileUploading(true);
setImageFileUploadError(null);
const storage = getStorage(firebase);
const fileName = new Date().getTime() + imageFile.name;
const storageRef = ref(storage, fileName);
const uploadTask = uploadBytesResumable(storageRef, imageFile);
uploadTask.on(
'state_changed',
(snapshot) => {
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
setImageFileUploadProgress(progress.toFixed(0));
},
() => {
setImageFileUploadError(
'Could not upload image (File must be less than 2MB)'
);
setImageFileUploadProgress(null);
setImageFile(null);
setImageFileUrl(null);
setImageFileUploading(false);
},
() => {
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
setImageFileUrl(downloadURL);
setFormData({ ...formData, profilePicture: downloadURL });
setImageFileUploading(false);
});
}
);
};
// Handle input change
const handleChange = (e) => {
const { id, value } = e.target;
setFormData({ ...formData, [id]: value });
// Dispatch action to update currentUser with new username value
if (id === 'username' && currentUser) {
dispatch(updateCurrentUser({ ...currentUser, username: value }));
}
};
const handleCreateSubmit = async (e) => {
e.preventDefault();
setCreateUserError(null);
setCreateUserSuccess(null);
console.log(formData);
if (Object.keys(formData).length === 0) {
setCreateUserError('No changes made');
return;
}
if (imageFileUploading) {
setCreateUserError('Please wait for image to upload');
return;
}
try {
dispatch(createStart());
const res = await fetch('/api/users/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
});
const data = await res.json();
if (!res.ok) {
dispatch(createFailure(data.message));
setCreateUserError(data.message);
} else {
dispatch(createSuccess(data));
setCreateUserSuccess("User's profile updated successfully");
navigate('/profile', { state: { formData } });
}
} catch (error) {
dispatch(createFailure(error.message));
setCreateUserError(error.message);
}
};
return (
<>
<BackgroundProfileContainer >
<form onSubmit={handleCreateSubmit}>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<DivImage>
<input
type='file'
accept='image/*'
onChange={handleImageChange}
ref={filePickerRef}
hidden
/>
<div
className='relative w-100 h-100 self-center cursor-pointer shadow-md overflow-hidden rounded-full'
onClick={() => filePickerRef.current.click()}
>
{imageFileUploadProgress && (
<CircularProgressbar
value={imageFileUploadProgress || 0}
text={`${imageFileUploadProgress}%`}
strokeWidth={5}
styles={{
root: {
width: '100%',
height: '100%',
position: 'absolute',
top: 0,
left: 0,
},
path: {
stroke: `rgba(62, 152, 199, ${
imageFileUploadProgress / 100
})`,
},
}}
/>
)}
<UserAvatar
profilePicture={imageFileUrl}
height={100}
width={100}
alt='user'
className={`rounded-full w-full h-full object-cover border-8 border-[lightgray] ${
imageFileUploadProgress &&
imageFileUploadProgress < 100 &&
'opacity-50'
}`}
/>
</div>
{imageFileUploadError && (
<Alert color='failure'>{imageFileUploadError}</Alert>
)}
</DivImage>
</div>
<span style={{ textShadow: "none", color: "#000" }} >
<StyledInput
type='text'
id='username'
placeholder='username...'
value={formData.username}
onChange={handleChange}
/>
</span>
<br/>
<span style={{ textShadow: "none", color: "#000" }} >
<div style={{
backgroundColor: '#9645ff',
borderRadius: '20px',
padding: '10px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
marginTop: '10px',
}}>
<input
type="date"
value={formData.date}
id="date"
onChange={handleChange}
style={{
border: 'none',
background: 'transparent',
color: '#000',
outline: 'none',
textAlign: 'center',
}}
/>
</div>
</span>
<br/>
<span style={{ textShadow: "none", color: "#000" }} >
<StyledInput
type="text"
id="about"
placeholder="about..."
onChange={handleChange}
/>
</span>
<br/>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<SaveBtnStyle
type='submit'
outline
disabled={loading || imageFileUploading}
>
{loading ? 'Loading...' : 'Create'}
</SaveBtnStyle>
</div>
<br/>
</form>
{createUserSuccess && (
<Alert color='success' className='mt-5'>
{createUserSuccess}
</Alert>
)}
{createUserError && (
<Alert color='success' className='mt-5'>
{createUserError}
</Alert>
)}
{error && (
<Alert color='failure' className='mt-5'>
{error}
</Alert>
)}
</BackgroundProfileContainer>
</>
);
}
export default NewAccountPage;
---
ProfileEditPage >
import React, { useEffect, useRef, useState } from "react";
import { Link, useLocation } from "react-router-dom";
//firebase
import { getAuth, signOut } from "firebase/auth";
import {
getDownloadURL,
getStorage,
ref,
uploadBytesResumable,
} from 'firebase/storage';
import { firebase } from '../Firebase';
//redux
import {
updateStart,
updateSuccess,
updateFailure,
deleteUserStart,
deleteUserSuccess,
deleteUserFailure,
} from '../redux/userSlice';
import { useSelector } from 'react-redux';
import { useDispatch } from 'react-redux';
import UserAvatar from '../Chat/UserAvatar';
// css
import { BackgroundProfileContainer, BackToListPage, LogOutLine,
StyledInput, DivImage, SaveBtnStyle } from "./profileElements";
import { CircularProgressbar } from 'react-circular-progressbar';
import { Alert, Button, Modal } from 'flowbite-react';
import { HiOutlineExclamationCircle } from 'react-icons/hi';
function ProfileEditPage() {
const { currentUser, error, loading } = useSelector((state) => state.user)|| {};
const location = useLocation();
const formDataFromNewAccount = location.state?.formData || {};
const [formData, setFormData] = useState(formDataFromNewAccount);
const [imageFile, setImageFile] = useState(null);
const [imageFileUrl, setImageFileUrl] = useState(null);
const [imageFileUploadProgress, setImageFileUploadProgress] = useState(null);
const [imageFileUploadError, setImageFileUploadError] = useState(null);
const [imageFileUploading, setImageFileUploading] = useState(false);
const [updateUserSuccess, setUpdateUserSuccess] = useState(null);
const [updateUserError, setUpdateUserError] = useState(null);
const [showModal, setShowModal] = useState(false);
const filePickerRef = useRef();
const dispatch = useDispatch();
useEffect(() => {
if (!currentUser) {
console.error('currentUser not available');
}
}, [currentUser]);
const handleImageChange = (e) => {
const file = e.target.files[0];
if (file) {
setImageFile(file);
setImageFileUrl(URL.createObjectURL(file));
setFormData({ ...formData, profilePicture: URL.createObjectURL(file) });
}
};
useEffect(() => {
if (imageFile) {
uploadImage();
}
}, [imageFile]);
useEffect(() => {
if (formDataFromNewAccount) {
setFormData({ ...formDataFromNewAccount });
}
}, []);
const uploadImage = async () => {
// service firebase.storage {
// match /b/{bucket}/o {
// match /{allPaths=**} {
// allow read;
// allow write: if
// request.resource.size < 2 * 1024 * 1024 &&
// request.resource.contentType.matches('image/.*')
// }
// }
// }
setImageFileUploading(true);
setImageFileUploadError(null);
const storage = getStorage(firebase);
const fileName = new Date().getTime() + imageFile.name;
const storageRef = ref(storage, fileName);
const uploadTask = uploadBytesResumable(storageRef, imageFile);
uploadTask.on(
'state_changed',
(snapshot) => {
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
setImageFileUploadProgress(progress.toFixed(0));
},
() => {
setImageFileUploadProgress(null);
setImageFile(null);
setImageFileUrl(null);
setImageFileUploading(false);
},
() => {
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
setImageFileUrl(downloadURL);
setFormData({ ...formData, profilePicture: downloadURL });
setImageFileUploading(false);
});
}
);
};
const handleChange = (e) => {
setFormData({ ...formData, [e.target.id]: e.target.value });
};
const handleUpdateSubmit = async (e) => {
e.preventDefault();
setUpdateUserError(null);
setUpdateUserSuccess(null);
console.log(formData);
if (Object.keys(formData).length === 0) {
setUpdateUserError('No changes made');
return;
}
if (imageFileUploading) {
setUpdateUserError('Please wait for image to upload');
return;
}
try {
dispatch(updateStart());
const res = await fetch(`/api/users/update/${currentUser._id}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
});
const data = await res.json();
if (!res.ok) {
dispatch(updateFailure(data.message));
setUpdateUserError(data.message);
} else {
dispatch(updateSuccess(data));
setUpdateUserSuccess("User's profile updated successfully");
}
} catch (error) {
dispatch(updateFailure(error.message));
setUpdateUserError(error.message);
}
};
const handleDeleteUser = async () => {
setShowModal(false);
try {
dispatch(deleteUserStart());
const res = await fetch(`/api/users/delete/${currentUser._id}`, {
method: 'DELETE',
});
const data = await res.json();
if (!res.ok) {
dispatch(deleteUserFailure(data.message));
} else {
dispatch(deleteUserSuccess(data));
}
} catch (error) {
dispatch(deleteUserFailure(error.message));
}
};
const LogOut = async () => {
try {
const auth = getAuth();
signOut(auth);
} catch (error) {
console.log(error);
}
}
return (
<>
<BackgroundProfileContainer >
<form onSubmit={handleUpdateSubmit}>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<DivImage>
<input
type='file'
accept='image/*'
onChange={handleImageChange}
ref={filePickerRef}
hidden
/>
<div
className='relative w-100 h-100 self-center cursor-pointer shadow-md overflow-hidden rounded-full'
onClick={() => filePickerRef.current.click()}
>
{imageFileUploadProgress && (
<CircularProgressbar
value={imageFileUploadProgress || 0}
text={`${imageFileUploadProgress}%`}
strokeWidth={5}
styles={{
root: {
width: '100%',
height: '100%',
position: 'absolute',
top: 0,
left: 0,
},
path: {
stroke: `rgba(62, 152, 199, ${
imageFileUploadProgress / 100
})`,
},
}}
/>
)}
<UserAvatar
profilePicture={formDataFromNewAccount.profilePicture || imageFileUrl || (formData && formData.profilePicture) || ''}
height={100}
width={100}
alt='user'
className={`rounded-full w-full h-full object-cover border-8 border-[lightgray] ${
imageFileUploadProgress &&
imageFileUploadProgress < 100 &&
'opacity-50'
}`}
/>
</div>
{imageFileUploadError && (
<Alert color='failure'>{imageFileUploadError}</Alert>
)}
</DivImage>
</div>
<span style={{ textShadow: "none", color: "#000" }} >
<StyledInput
type='text'
id='username'
placeholder='username...'
value={formData.username}
onChange={handleChange}
/>
</span>
<br/>
<span style={{ textShadow: "none", color: "#000" }} >
<div style={{
backgroundColor: '#00ffa6',
borderRadius: '20px',
padding: '10px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
marginTop: '10px',
}}>
<input
type="date"
value={formData.date}
id="date"
onChange={handleChange}
style={{
border: 'none',
background: 'transparent',
color: '#000',
outline: 'none',
textAlign: 'center',
}}
/>
</div>
</span>
<span style={{ textShadow: "none", color: "#000" }} >
<StyledInput
type="text"
id="about"
placeholder="about..."
value={formData.about}
onChange={handleChange}
/>
</span>
<br/>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<SaveBtnStyle
type='submit'
outline
disabled={loading || imageFileUploading}
>
{loading ? 'Loading...' : 'Update'}
</SaveBtnStyle>
</div>
<br/>
</form>
<br/>
<Link to="/listUsers">
<BackToListPage>
Back to ListPage
</BackToListPage>
</Link>
<Link to="/startForm" onClick={LogOut}>
<LogOutLine>
Log out
</LogOutLine>
</Link>
<div className='text-red-500 flex justify-between mt-5'>
<span onClick={() => setShowModal(true)} className='cursor-pointer'>
Delete Account
</span>
</div>
{updateUserSuccess && (
<Alert color='success' className='mt-5'>
{updateUserSuccess}
</Alert>
)}
{updateUserError && (
<Alert color='failure' className='mt-5'>
{updateUserError}
</Alert>
)}
{error && (
<Alert color='failure' className='mt-5'>
{error}
</Alert>
)}
<Modal
show={showModal}
onClose={() => setShowModal(false)}
popup
size='md'
>
<Modal.Header />
<Modal.Body>
<div className='text-center'>
<HiOutlineExclamationCircle className='h-14 w-14 text-gray-400 dark:text-gray-200 mb-4 mx-auto' />
<h3 className='mb-5 text-lg text-gray-500 dark:text-gray-400'>
Are you sure you want to delete your account?
</h3>
<div className='flex justify-center gap-4'>
<Button color='failure' onClick={handleDeleteUser}>
Yes, Iam sure
</Button>
<Button color='gray' onClick={() => setShowModal(false)}>
No, cancel
</Button>
</div>
</div>
</Modal.Body>
</Modal>
</BackgroundProfileContainer>
</>
);
}
export default ProfileEditPage;
---
userSlice.jsx >
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
currentUser: null,
error: null,
loading: false,
};
const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
createStart: (state) => {
state.loading = true;
state.error = null;
},
createSuccess: (state, action) => {
state.currentUser = action.payload;
state.loading = false;
state.error = null;
},
createFailure: (state, action) => {
state.loading = false;
state.error = action.payload;
},
updateStart: (state) => {
state.loading = true;
state.error = null;
},
updateSuccess: (state, action) => {
state.currentUser = action.payload;
state.loading = false;
state.error = null;
},
updateFailure: (state, action) => {
state.loading = false;
state.error = action.payload;
},
deleteUserStart: (state) => {
state.loading = true;
state.error = null;
},
deleteUserSuccess: (state) => {
state.currentUser = null;
state.loading = false;
state.error = null;
},
deleteUserFailure: (state, action) => {
state.loading = false;
state.error = action.payload;
},
updateCurrentUser: (state, action) => {
state.currentUser = action.payload;
},
},
});
export const {
createStart,
createSuccess,
createFailure,
updateStart,
updateSuccess,
updateFailure,
deleteUserStart,
deleteUserSuccess,
deleteUserFailure,
updateCurrentUser,
} = userSlice.actions;
export default userSlice.reducer;
---
store.jsx >
import { configureStore, combineReducers } from '@reduxjs/toolkit';
import userReducer from './userSlice';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
const rootReducer = combineReducers({
user: userReducer,
});
// Configuration for persisting the user reducer
const persistConfig = {
key: 'root',
storage,
version: 1,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
export const store = configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({ serializableCheck: false }),
});
export const persistor = persistStore(store);
---
user.js (backend logic) >
import Profile from "../models/Profile.js";
export const createUser = async (req, res) => {
try {
const newUser = new Profile({
username: req.body.username,
date: req.body.date,
about: req.body.about,
profilePicture: req.body.profilePicture,
});
const savedUser = await newUser.save();
res.status(201).json({savedUser});
console.log({savedUser})
} catch (error) {
if (error.code === 11000 && error.keyPattern && error.keyPattern.username) {
return res.status(400).json({ error: "Username already exists" });
}
console.error(error);
res.status(500).json({ error: 'Error creating user' });
}
};
export const getUser = async (req, res) => {
const id = req.params.id;
try {
const user = await Profile.findById(id);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.status(200).json({ user });
} catch (error) {
console.error(error);
res.status(500).json({ error: 'Error fetching user' });
}
};
export const updateUser = async (req, res) => {
const id = req.params.id;
try {
const updatedUser = await Profile.findByIdAndUpdate(
id,
{
$set: {
username: req.body.username,
date: req.body.date,
about: req.body.about,
profilePicture: req.body.profilePicture,
},
},
{ new: true }
);
return res.status(200).json(updatedUser);
} catch (error) {
console.error(error);
return res.status(500).json({ error: 'Error updating profile' });
}
};
export const deleteUser = async (req, res) => {
const id = req.params.id;
try {
const user = await Profile.findByIdAndDelete(id);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.status(200).json({ message: 'User has been deleted', user });
} catch (error) {
console.error(error);
res.status(500).json({ error: 'Error deleting profile' });
}
};
Thank you for your precious time to reiew my code. I am willingly to learn from my mistakes
I try to update data in ProfileEditPage for saving data in mongoDB and when comeback to this page again it would show the same data after I updated