I have a form that takes user data as prop
from parent component to update, updates them then sets the state of parent, but parent component doesn’t re-render when userData
state changes so i have to refresh the page to see the updates
Parent component
useEffect(() => {
const getUsers = async () => {
const res = await fetch("http://localhost:8000/api/users", {
credentials: "include",
});
const data = await res.json();
if (data.status === 401) {
dispatch({ type: "LOGOUT" });
navigate("/login");
}
setUsers(data.users);
// setUsers(data.users.filter((u) => u.id != 80));
};
getUsers();
}, [dispatch, navigate, user?.id]);
let emptyUser = {
id: "",
firstName: "",
lastName: "",
position: "",
gender: "",
birthDate: "",
status: "",
isAdmin: "",
image: null,
url: "",
};
const [userData, setUserData] = useState(emptyUser);
const editUser = (user) => {
setUserData(user);
};
Child component
function UpdateUser({ userData, editditUser }) {
const onSelectFile = (e) => {
if (!e.target.files || e.target.files.length === 0) {
editUser({ ...userData, image: undefined });
return;
}
editUser({ ...userData, image: e.target.files[0] });
setDisableBtn(false);
};
const onInputChange = (e, variable) => {
const val = (e.target && e.target.value) || "";
let _user = { ...userData };
_user[`${variable}`] = val;
editUser(_user);
setDisableBtn(false);
};
const editUser = async (e) => {
e.preventDefault();
let errors = {};
if (
userData.firstName.length > 15 ||
userData.lastName.length > 15 ||
userData.firstName.length < 3 ||
userData.lastName.length < 3
) {
errors.errName =
"First name and/or must be between 3 and 15 characters in length.";
}
if (!userData.gender) {
errors.errGender = "Gender must not be empty.";
}
if (!userData.status) {
errors.errStatus = "Status must not be empty.";
}
setErrors(errors);
if (Object.keys(errors).length > 0) {
return;
}
setDisableBtn(true);
let url;
if (preview) {
const imageRef = storageRef(storage, `images/${Date.now()}`);
const snapshot = await uploadBytes(imageRef, userData.image);
url = await getDownloadURL(snapshot.ref);
}
const userToUpdate = {
firstName: userData.firstName,
lastName: userData.lastName,
email: userData.email,
position: userData.position,
gender: userData.gender,
birthDate: userData.birthDate,
image: url || userData.image,
status: userData.status,
isAdmin: userData.isAdmin === "Admin" ? 1 : 0,
};
const res = await fetch(
`http://localhost:8000/api/edit-user/${userData.id}`,
{
credentials: "include",
method: "PUT",
body: JSON.stringify(userToUpdate),
headers: { "Content-Type": "application/json" },
}
);
const data = await res.json();
if (data.status === 401) {
dispatch({ type: "LOGOUT" });
navigate("/login");
}
if (data.status === 400) {
setDisableBtn(false);
setErrUser(data.message);
return false;
}
console.log("success");
};
}
2
Answers
IIUC, the Parent Component fetches a list of users (from Backend), and holds a
userData
state to edit one of these users when needed. ThisuserData
state is shared with the Child Component, which displays the edit form and handles the request to Backend to save the updated user data when the form is submitted.But the list in the Parent does not reflect that change for that user, because the displayed data (
users
) is not the state (user
) that has been modified.In that case, you can display the updated user in several ways, depending on how your Parent JSX is structured and how it calls the Child.
Given the code you share, one of the solutions could be to re-fetch the entire list of users after the update request succeeds. The added advantage is that you are sure to retrieve what the Backend actually stored (it may sanitize / reject some updates):
When you ask something like this, you should include a Minimal reproducible example, leaving behind all the variables that are not necessary to the problem (e.g. navigate function, or dispatch function)
Since you haven’t included the complete components, i have to make a supposition here: when you call your UpdateUser component you are passing userData and the editUser as props. In this case, there a 2 errors. First, you need to include userData in your useEffect dependencies array, otherwise it will not update when userData changes. Then, in your UpdateUser component you never call the editditUser function passed as prop. Here there’s a minimal code to do what you want
Parent component:
Child component: