React中未将数据作为prop传递

huangapple go评论66阅读模式
英文:

Data not being passed down as a prop in React

问题

I understand that you're facing an issue with your MERN stack application related to editing user details, and you've provided a lot of code and context. If you have a specific question or need assistance with a particular part of your code, please let me know, and I'll do my best to help you.

英文:

So for some context, I am building a small website using the mern stack (React, Redux toolkit, Mongo, Express) for my father and his dealership. I am running into a problem on the front end with regards to my edit user form. currently I have not configured jwt tokens or any protected routes, that will be coming in the future. however I have created the ability to create user account detail form which is sent to the backend and is processed. This ties into my problem which is the edit user form.

When at the /users in the url on my frontend you are presented with a page with a list of all the users and their specific details. I also have an edit button which should, once clicked, put you to the edit user form page so that you can edit that users details. This is where I have run into my problem. When I click on edit on someone with a dealership role (there are three roles, dealership, admin and customer) you can edit their details no problem, it works the logic is correct and does everything as expected. when you try to do the same with anyone with either the admin or customer role it does not work it throws an error which you will see in the images below. Im guessing this error is due to the data not being passed down correctly, hence reading undefined. some of the below images are blurred to hide sensitive information of users.

users list page
when you click on edit for someone with the dealership role it works just fine as the below image shows.
User with dealership role on edit user form
Now when you click on edit on someone with any other role you get the below error.
Error on Non dealership roles

Here is the code for the EditUser.js

import { useParams } from "react-router-dom";
import { useSelector } from "react-redux";
import { selectUserById } from "./usersApiSlice";
import EditUserForm from "./EditUserForm";

const EditUser = () => {
  const { id } = useParams();

  const user = useSelector((state) => selectUserById(state, id));

  const content = user ? <EditUserForm user={user} /> : <p>Loading...</p>;

  return content;
};

export default EditUser;

Here is the code for EditUserForm.js

import { useState, useEffect } from "react";
import { useUpdateUserMutation, useDeleteUserMutation } from "./usersApiSlice";
import { useNavigate } from "react-router-dom";
import { ROLES } from "../../config/roles";
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const PWD_REGEX = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*\W).{8,}$/;
const EditUserForm = ({ user }) => {
const [updateUser, { isLoading, isSuccess, isError, error }] =
useUpdateUserMutation();
const [
deleteUser,
{ isSuccess: isDelSuccess, isError: isDelError, error: delerror },
] = useDeleteUserMutation();
const navigate = useNavigate();
const [email, setEmail] = useState(user.email);
const [validEmail, setValidEmail] = useState(false);
const 
输入密码查看隐藏内容

= useState(""); const [validPassword, setValidPassword] = useState(false); const [firstname, setFirstname] = useState(user.firstname); const [lastname, setLastname] = useState(user.lastname); const [telephone, setTelephone] = useState(user.telephone); const [roles, setRoles] = useState(user.roles); const [profilePicture, setProfilePicture] = useState(user.profilePicture); const [companyName, setCompanyName] = useState(user.companyName); useEffect(() => { if (isSuccess || isDelSuccess) { setEmail(""); setPassword(""); setRoles([]); setFirstname(""); setLastname(""); setTelephone(""); setProfilePicture(""); setCompanyName(""); navigate("/dashboard/settings"); } }, [isSuccess, isDelSuccess, navigate]); useEffect(() => { setValidEmail(EMAIL_REGEX.test(email)); }, [email]); useEffect(() => { setValidPassword(PWD_REGEX.test(password)); },
输入密码查看隐藏内容

); const onEmailChanged = (e) => setEmail(e.target.value); const onPasswordChanged = (e) => setPassword(e.target.value); const onRolesChanged = (e) => { const values = Array.from( e.target.selectedOptions, //HTMLCollection (option) => option.value ); setRoles(values); }; const onFirstnameChanged = (e) => setFirstname(e.target.value); const onLastnameChanged = (e) => setLastname(e.target.value); const onTelephoneChanged = (e) => setTelephone(e.target.value); const onProfilePictureChanged = (e) => setProfilePicture(e.target.value); const onCompanyNameChanged = (e) => setCompanyName(e.target.value); })); }; const onSaveUserClicked = async (e) => { if (password) { await updateUser({ id: user.id, email, password, roles, firstname, lastname, telephone, profilePicture, }); } else { await updateUser({ id: user.id, email, roles, firstname, lastname, telephone, profilePicture, companyName, }); } }; const onDeleteUserClicked = async () => { await deleteUser({ id: user.id }); }; const options = Object.values(ROLES).map((role) => { return ( <option key={role} value={role}> {" "} {role} </option> ); }); let canSave; if (password) { canSave = roles.length > 0 && password && validEmail && validPassword && firstname.trim().length > 0 && lastname.trim().length > 0 && (telephone.trim().length === 0 || /^[0-9]+$/.test(telephone)) && !isLoading; } else { canSave = roles.length > 0 && validEmail && firstname.trim().length > 0 && lastname.trim().length > 0 && (telephone.trim().length === 0 || /^[0-9]+$/.test(telephone)) && !isLoading; } const errClass = isError || isDelError ? "errmsg" : "offscreen"; const validEmailClass = validEmail ? "" : "form__input--incomplete"; const validPwdClass = validPassword ? "" : "form__input--incomplete"; const validRolesClass = roles.length > 0 ? "" : "form__input--incomplete"; const validFirstNameClass = firstname.trim().length > 0 ? "" : "form__input--incomplete"; const validLastNameClass = lastname.trim().length > 0 ? "" : "form__input--incomplete"; const validTelephoneClass = telephone.trim().length === 0 || /^[0-9]+$/.test(telephone) ? "" : "form__input--incomplete"; const validCompanyNameClass = !Object.values(ROLES).includes("Dealership") || companyName.trim().length > 0 ? "" : "form__input--incomplete"; const validProfilePictureClass = profilePicture.trim().length > 0 ? "" : "form__input--incomplete"; const errContent = (error?.data?.message || delerror?.data?.message) ?? ""; const content = ( <> <p className={errClass}>{errContent}</p> <form className="editUserForm" onSubmit={onSaveUserClicked}> <h2 className="signUp">EditUserForm</h2> <div className="inputFields"> <label className="form__label--visually-hidden" htmlFor="email"> Email: <span className="nowrap">[Must include @]</span> </label> <input className={`form__input ${validEmailClass}`} id="email" name="email" type="text" autoComplete="off" value={email} onChange={onEmailChanged} onFocus={(e) => { if (e.target.value === "Email") { e.target.value = ""; } }} onBlur={(e) => { if (e.target.value === "") { e.target.value = "Email"; } }} placeholder="Email" /> <label className="form__label--visually-hidden" htmlFor="roles"> ASSIGNED ROLES: </label> <select id="roles" name="roles" className={`form__select ${validRolesClass}`} multiple={true} size="3" value={roles} onChange={onRolesChanged} > {options} </select> <div className="form__action-buttons"> <button className="icon-button" title="Save" disabled={!canSave} onClick={onSaveUserClicked} > Save </button> <button className="icon-button" title="delete" onClick={onDeleteUserClicked} > delete </button> </div> </form> </> ); return content; }; export default EditUserForm;

I have removed part of the form as it takes up a lot of space, most of the fields are like the email field above. Here is my roles.js file too.

export const ROLES = {
Customer: "Customer",
Dealership: "Dealership",
Admin: "Admin",
};

If there are any other files you would like to see please let me know such as the usersApiSlice.js, store.js etc. if you would like to see file tree please do let me know. I would like to mention I have tested my backend with postman with all crud operations. I also do not have errors on my error middleware loggers. It seems like the backend is not the issue but rather the front end. I appreciate if anyone has taken the time to read this.

here is my store.js

import { configureStore } from "@reduxjs/toolkit";
import { apiSlice } from "./api/apiSlice";
import { setupListeners } from "@reduxjs/toolkit/dist/query";
export const store = configureStore({
reducer: {
[apiSlice.reducerPath]: apiSlice.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(apiSlice.middleware),
devTools: true,
});
setupListeners(store.dispatch);

Here is my usersApiSlice.js

import { createSelector, createEntityAdapter } from     "@reduxjs/toolkit";
import { apiSlice } from "../../app/api/apiSlice";
const usersAdapter = createEntityAdapter({});
const initialState = usersAdapter.getInitialState();
export const usersApiSlice = apiSlice.injectEndpoints({
endpoints: (builder) => ({
getUsers: builder.query({
query: () => "/users",
validateStatus: (response, result) => {
return response.status === 200 && !result.isError;
},
transformResponse: (responseData) => {
const loadedUsers = responseData.map((user) => {
user.id = user._id;
return user;
});
return usersAdapter.setAll(initialState, loadedUsers);
},
providesTags: (result, error, arg) => {
if (result?.ids) {
return [
{ type: "User", id: "LIST" },
...result.ids.map((id) => ({ type: "User", id })),
];
} else return [{ type: "User", id: "LIST" }];
},
}),
addNewUser: builder.mutation({
query: (initialUserData) => ({
url: "/users",
method: "POST",
body: {
...initialUserData,
},
}),
invalidatesTags: [{ type: "User", id: "LIST" }],
}),
updateUser: builder.mutation({
query: (initialUserData) => ({
url: "/users",
method: "PATCH",
body: {
...initialUserData,
},
}),
invalidatesTags: (result, error, arg) => [{ type: "User", id: arg.id }],
}),
deleteUser: builder.mutation({
query: ({ id }) => ({
url: `/users`,
method: "DELETE",
body: { id },
}),
invalidatesTags: (result, error, arg) => [{ type: "User", id: arg.id }],
}),
}),
});
export const {
useGetUsersQuery,
useAddNewUserMutation,
useUpdateUserMutation,
useDeleteUserMutation,
} = usersApiSlice;
// returns the query result object
export const selectUsersResult =   usersApiSlice.endpoints.getUsers.select();
// creates memoized selector
const selectUsersData = createSelector(
selectUsersResult,
(usersResult) => usersResult.data 
// normalized state object with ids & entities
);
//getSelectors creates these selectors and we rename them
with aliases using destructuring
export const {
selectAll: selectAllUsers,
selectById: selectUserById,
selectIds: selectUserIds,
// Pass in a selector that returns the users slice of state
} = usersAdapter.getSelectors(
(state) => selectUsersData(state) ?? initialState
);

I tried things such as testing my backend, googling the error and my problem and things of that sort of nature. I even tried using ai to help but any solutions provided did not work.

答案1

得分: 0

我收到undefined的原因是表单是一个适用于所有用户的通用表单,而具有不同角色的用户具有额外的数据字段。这意味着表单在某些部分,如companyName,上读取undefined,因为客户没有该字段。这解释了为什么表单只在经销商角色上起作用,而在其他任何角色上都不起作用,因为尝试访问不存在的数据,因此读取undefined。考虑为不同角色使用单独的表单,并有条件地将用户传递给这些表单

const content = user ? (
  user.role === "dealership" ? (
    <EditDealerForm user={user} />
  ) : (
    <EditUserForm user={user} />
  )
) : (
  <p>Loading...</p>
);
英文:

I was receiving undefined because the form was a one size fits all for all users, when users with different roles have extra bits of data fields. this meant that the form was reading undefined on certain parts such as companyName because the customer doesn't have that. this explains why the form was only working on the dealership role and not any other role because data that was not there was trying to be accessed hence reading undefined. Consider having separate forms for different roles and pass the the user down to these forms conditionally

  const content = user ? (
user.role === &quot;dealership&quot; ? (
&lt;EditDealerForm user={user} /&gt;
) : (
&lt;EditUserForm user={user} /&gt;
)

) : (
<p>Loading...</p>
);

huangapple
  • 本文由 发表于 2023年5月14日 01:47:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/76244145.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定