Итак, для некоторого контекста я создаю небольшой веб-сайт, используя стек мернов (React, инструментарий Redux, Mongo, Express) для моего отца и его дилерского центра. Я столкнулся с проблемой на переднем конце в отношении моей формы редактирования пользователя. в настоящее время я не настроил токены jwt или какие-либо защищенные маршруты, которые появятся в будущем. однако я создал возможность создания формы сведений об учетной записи пользователя, которая отправляется в серверную часть и обрабатывается. Это связано с моей проблемой, которая представляет собой форму редактирования пользователя.
Когда на /users в URL-адресе моего интерфейса вам предоставляется страница со списком всех пользователей и их конкретными данными. У меня также есть кнопка редактирования, которая после нажатия должна перевести вас на страницу редактирования пользовательской формы, чтобы вы могли редактировать данные этого пользователя. Вот и я столкнулся со своей проблемой. Когда я нажимаю «Изменить» для кого-то с ролью дилера (есть три роли: дилер, администратор и клиент), вы можете редактировать их данные без проблем, это работает, логика правильная и все происходит так, как ожидалось. когда вы пытаетесь сделать то же самое с кем-либо с ролью администратора или клиента, это не работает, выдает ошибку, которую вы увидите на изображениях ниже. Я предполагаю, что эта ошибка связана с тем, что данные не передаются правильно, поэтому чтение не определено. некоторые из приведенных ниже изображений размыты, чтобы скрыть конфиденциальную информацию пользователей.
страница со списком пользователей когда вы нажимаете «Изменить» для кого-то с ролью дилера, все работает нормально, как показано на изображении ниже. Пользователь с ролью дилера в форме редактирования пользователя Теперь, когда вы нажимаете «Изменить» для кого-то с любой другой ролью, вы получаете следующую ошибку. Ошибка на недилерских ролях
Вот код для 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;
Вот код для 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 [password, setPassword] = 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));
}, [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;
Я удалил часть формы, так как она занимает много места, большинство полей похожи на поле электронной почты выше. Вот и мой файл roles.js.
export const ROLES = {
Customer: "Customer",
Dealership: "Dealership",
Admin: "Admin",
};
Если есть какие-либо другие файлы, которые вы хотели бы увидеть, сообщите мне, например, usersApiSlice.js, store.js и т. д., если вы хотите увидеть дерево файлов, пожалуйста, дайте мне знать. Я хотел бы упомянуть, что я протестировал свой бэкэнд с почтальоном со всеми грубыми операциями. У меня также нет ошибок в моих регистраторах ошибок промежуточного программного обеспечения. Кажется, что проблема не в бэкэнде, а в переднем конце. Я ценю, если кто-то нашел время, чтобы прочитать это.
вот мой 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);
Вот мои пользователиApiSlice.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
);
Я пробовал такие вещи, как тестирование моего бэкенда, гугление ошибок и моей проблемы и тому подобное. Я даже пытался использовать ИИ, чтобы помочь, но любые предложенные решения не сработали.
Похоже selectUserById возвращается undefined
Таким образом, допустимый класс названия компании просто проверяет, являетесь ли вы дилером, вы должны ввести название компании в форму, иначе форма будет неполной, а form__inpute — неполное имя класса будет добавлено в форму, для которой у меня есть CSS для
Странно то, что эта ошибка возникает только у пользователей со статусом администратора или клиента, @Konrad. С дилерами работает нормально. Я думаю, что вы правы, хотя, как бы я проверил, если это так.
Рассмотрите возможность добавления кода этой функции здесь и, возможно, кода хранилища избыточности.
@Konrad да, отредактировал пост, он внизу
Вы проверили вкладку сети в инструментах разработчика, чтобы увидеть, одинаковы ли ответы для разных ролей? К сожалению, я не вижу здесь ничего, что могло бы вызвать эту ошибку.
Я попробую это, и я буду держать вас в курсе, спасибо за вашу помощь, братан, очень ценю это.
я получаю статус 200 для пользователей. никаких 404 и других ошибок. Я ценю вашу помощь, человек, это одна странная проблема. плохо разберусь в конце концов 😅





Я получал undefined, потому что форма была универсальной для всех пользователей, когда у пользователей с разными ролями были дополнительные биты полей данных. это означало, что форма читалась неопределенной в некоторых частях, таких как companyName, потому что у клиента ее нет. это объясняет, почему форма работала только с ролью дилера, а не с какой-либо другой ролью, потому что данные, которых там не было, пытались получить доступ, поэтому читались как неопределенные. Подумайте о том, чтобы иметь отдельные формы для разных ролей и условно передавать пользователя этим формам.
const content = user ? (
user.role === "dealership" ? (
<EditDealerForm user = {user} />
) : (
<EditUserForm user = {user} />
)
) : (
Загрузка...
);
можете ли вы объяснить const validCompanyNameClass = !Object.values(ROLES).includes("Dealership") || companyName.trim().length > 0 ? "" : "form__input--incomplete"; этот код вкратце. Ошибка может быть связана с некоторым свойством, к которому вы пытаетесь получить доступ для определенной роли, и оно не определено. Например, администратор и клиент могут не иметь связанного с ними названия компании.