Вопрос или проблема
Что у меня есть:
Я создал страницу для обновления продуктов в своем приложении на React, используя свои собственные компоненты.
У продукта есть следующие поля:
- Id
- Название
- Код
- Тип продукта
- Компания
Что я хочу сделать:
Я хочу загружать данные в другие поля, когда выбираю продукт из своего пользовательского селектора.
Что происходит: Я могу загружать данные в поля кода и названия, которые являются простыми текстовыми полями, но не могу выбрать соответствующую компанию и тип продукта в своем пользовательском селекторе. Он оказывается пустым.
Вот мой код для EntitySelector.jsx:
import { MenuItem, Select } from "@mui/material";
import { useEffect, useState } from "react";
import { fetchData } from "erp/api/util/DataFetchUtil";
import 'erp/styles/common/EntitySelector.css';
export const EntitySelector = (props) => {
const [entities, setEntities] = useState([]);
// Получение данных всякий раз, когда refresh равно true
useEffect(() => {
if (props.refresh) {
fetchData(props.fetchOperationId, props.apiPayload, setEntities);
props.setRefresh(false);
}
}, [props.refresh, props.fetchOperationId, props.apiPayload]);
// Получение данных при начальной загрузке
useEffect(() => {
fetchData(props.fetchOperationId, props.apiPayload, setEntities);
}, [props.fetchOperationId, props.apiPayload]);
const handleChange = (event) => {
// Находим выбранную сущность по ID
const selectedEntity = entities.find(entity => entity.id === event.target.value);
props.setSelectedEntity(selectedEntity);
};
return (
<>
<Select
className="entitySelector"
value={props.selectedEntity ? props.selectedEntity.id : ""}
onChange={handleChange}
displayEmpty
variant="outlined"
>
<MenuItem disabled value="">
<em>Выберите {props.entityName}</em>
</MenuItem>
{entities.map((entity) => (
<MenuItem
key={entity.id}
value={entity.id}
selected={props.selectedEntity ? entity.id === props.selectedEntity.id : false}>
{entity.name}
</MenuItem>
))}
</Select>
</>
);
};
А вот код для экрана обновления:
import { Checkbox, Typography } from "@mui/material";
import { addProducts, updateProduct } from "erp/api/inventory/ProductService";
import { ActionButton } from "erp/components/common/ActionButton";
import { ContainerHeading } from "erp/components/common/ContainerHeading";
import { CustomInput } from "erp/components/common/CustomInput";
import { EntitySelector } from "erp/components/common/EntitySelector";
import { Message } from "erp/components/common/Message";
import { VIEW_COMPANY_OPERATION_ID, VIEW_PRODUCT_TYPE_OPERATION_ID, VIEW_PRODUCTS_OPERATION_ID } from "erp/constants/OperationsInfo";
import ProductDTO from "erp/dto/ProductDTO";
import { isInputEmpty } from "erp/util/InputUtil";
import { useEffect, useState } from "react";
export const EditProductScreen = () => {
const [selectedProduct, setSelectedProduct] = useState({});
const [selectedCompany, setSelectedCompany] = useState({});
const [selectedProductType, setSelectedProductType] = useState({});
const [productName, setProductName] = useState('');
const [productCode, setProductCode] = useState('');
const [message, setMessage] = useState('');
const [expiryApplicable, setExpiryApplicable] = useState(true);
useEffect(() => {
if (selectedProduct) {
const company = selectedProduct.company || {};
const productType = selectedProduct.productType || {};
setSelectedCompany(company);
console.log("Тест");
console.log(selectedCompany);
setSelectedProductType(productType);
setExpiryApplicable(selectedProduct.isExpiryApplicable);
setProductName(selectedProduct.name);
setProductCode(selectedProduct.productCode);
}
}, [selectedProduct]);
const handleExpiryApplicable = (event) => {
setExpiryApplicable(event.target.checked);
};
const setProductNameAction = (event) => {
setProductName(event.target.value);
};
const setProducCodeAction = (event) => {
setProductCode(event.target.value);
};
const handleEditProduct = async () => {
if (isInputEmpty(selectedProduct)) {
setMessage({
messageType: "ERR",
txt: `Ошибка: Пожалуйста, выберите компанию`
});
return;
}
if (isInputEmpty(selectedCompany)) {
setMessage({
messageType: "ERR",
txt: `Ошибка: Пожалуйста, выберите компанию`
});
return;
}
if (isInputEmpty(selectedProductType)) {
setMessage({
messageType: "ERR",
txt: `Ошибка: Пожалуйста, выберите тип продукта`
});
return;
}
if (isInputEmpty(productName)) {
setMessage({
messageType: "ERR",
txt: `Ошибка: Пожалуйста, введите название продукта`
});
return;
}
if (isInputEmpty(productCode)) {
setMessage({
messageType: "ERR",
txt: `Ошибка: Пожалуйста, введите код продукта`
});
return;
}
const product = new ProductDTO();
let productId = selectedProduct.id;
product.companyId = selectedCompany.id ;
product.productTypeId = selectedProductType.id;
product.name = productName;
product.productCode = productCode;
product.isExpiryApplicable = expiryApplicable;
const payload = JSON.stringify(product);
try {
const responseData = await updateProduct(productId, payload);
if (responseData.responseCode === 20) {
setMessage({
messageType: "MSG",
txt: responseData.message
});
} else {
setMessage({
messageType: "DETAILED_ERR",
txt: `Ошибка: ${responseData.message}`
});
}
} catch (error) {
console.error('Ошибка при обновлении продукта:', error);;
setMessage({
messageType: "ERR",
txt: `Ошибка: Произошла ошибка. Пожалуйста, попробуйте еще раз.`
});
}
};
let req = {
fetchProductCount: false,
fetchId: true
}
return (
<div className="addEntityContainer">
<ContainerHeading heading = "h4" headingTxt = "Редактировать продукт"/>
<EntitySelector
entityName="Продукт"
fetchOperationId={VIEW_PRODUCTS_OPERATION_ID}
selectedEntity={selectedProduct}
setSelectedEntity={setSelectedProduct}
apiPayload = {{fetchAllData: true}}
/>
<EntitySelector
entityName="Компания"
fetchOperationId={VIEW_COMPANY_OPERATION_ID}
selectedEntity={selectedCompany}
setSelectedEntity={setSelectedCompany}
apiPayload = {req}
/>
<EntitySelector
entityName="Тип продукта"
fetchOperationId={VIEW_PRODUCT_TYPE_OPERATION_ID}
selectedEntity={selectedProductType}
setSelectedEntity={setSelectedProductType}
apiPayload = {req}
/>
<CustomInput
inputType="text"
inputValue = {productName}
inputPlaceholder="Введите название продукта"
isRequired = {true}
changeHandler = {setProductNameAction}
/>
<CustomInput
inputType="text"
inputValue = {productCode}
inputPlaceholder="Введите код продукта"
isRequired = {true}
changeHandler = {setProducCodeAction}
/>
<div style={{display: "flex", flexDirection: "row"}}>
<Typography variant="body1" sx={{alignContent: 'center', color: 'white'}}>Срок действия применим</Typography>
<Checkbox
defaultChecked
style={{ color: '#fff' }}
checked={expiryApplicable}
onChange={handleExpiryApplicable}
/>
</div>
<ActionButton handleClick={handleEditProduct} buttonText = "Добавить продукт"/>
<Message message={message}/>
</div>
);
}
Как решить эту проблему? Во-вторых, всякий раз, когда EntitySelector обновляется, он вызывает API для получения сущностей. Как этого избежать?
Ответ или решение
Чтобы решить вашу проблему с выбором правильных элементов в компоненте EntitySelector
и избеганием лишних запросов API, следуйте следующим шагам:
Проблема 1: Заполнение полей "Company" и "Product Type"
-
Проблема обновления состояния: Убедитесь, что состояние
selectedProduct
обновляется правильно, и при его изменении значенияselectedCompany
иselectedProductType
устанавливаются корректно.В вашем коде необходимо передать актуальные
company
иproductType
изselectedProduct
. Можете добавить проверку на наличиеselectedProduct
, чтобы избежать ошибок.Одно из возможных исправлений может выглядеть следующим образом:
useEffect(() => { if (Object.keys(selectedProduct).length > 0) { const { company = {}, productType = {}, name, productCode, isExpiryApplicable } = selectedProduct; setSelectedCompany(company); setSelectedProductType(productType); setExpiryApplicable(isExpiryApplicable); setProductName(name); setProductCode(productCode); } }, [selectedProduct]);
-
Проблемы с передачей значений из
EntitySelector
: Убедитесь, чтоEntitySelector
для компаний и типов продуктов правильно передает выбранное значение. Убедитесь, что для этих селекторов установлено правильное значение:<EntitySelector entityName="Company" fetchOperationId={VIEW_COMPANY_OPERATION_ID} selectedEntity={selectedCompany} setSelectedEntity={setSelectedCompany} apiPayload={req} /> <EntitySelector entityName="Product Type" fetchOperationId={VIEW_PRODUCT_TYPE_OPERATION_ID} selectedEntity={selectedProductType} setSelectedEntity={setSelectedProductType} apiPayload={req} />
Проблема 2: Частые запросы к API при обновлении EntitySelector
Для предотвращения постоянных запросов к API при каждом обновлении EntitySelector
, вы можете добавить состояние загрузки и проверять, уже ли были загружены данные. Можно изменить ваше текущее состояние сущностей на:
const [entitiesFetched, setEntitiesFetched] = useState(false);
Затем в useEffect
, который отвечает за загрузку данных, добавьте условие:
useEffect(() => {
if (props.refresh) {
if (!entitiesFetched) {
fetchData(props.fetchOperationId, props.apiPayload, setEntities);
props.setRefresh(false);
setEntitiesFetched(true); // Установить, что данные были загружены
}
}
}, [props.refresh, props.fetchOperationId, props.apiPayload, entitiesFetched]);
Таким образом, данные будут загружены только один раз, когда состояние entitiesFetched
ещё не было установлено в true
.
Общее решение
При использовании состояния и useEffect
, гарантируйте, что у вас нет лишних вызовов для обновления. Если состояние уже объединило нужные значения (company
и productType
), просто обновите их при выборе продукта. Убедитесь также, что ваши EntitySelector
, используемые для выбора Company
и Product Type
, получают корректные selectedEntity
и устанавливают их правильно.
Итоги
Эти изменения помогут вам правильно заполнять поля "Company" и "Product Type", а также сократят количество ненужных запросов к вашему API. Убедитесь, что для каждой сущности вы работаете с актуальными значениями и контролируете состояние загрузки.