Как выбрать MenuItem на основе пропсов в React?

Вопрос или проблема

Что у меня есть:
Я создал страницу для обновления продуктов в своем приложении на React, используя свои собственные компоненты.
У продукта есть следующие поля:

  1. Id
  2. Название
  3. Код
  4. Тип продукта
  5. Компания

Что я хочу сделать:
Я хочу загружать данные в другие поля, когда выбираю продукт из своего пользовательского селектора.

Что происходит: Я могу загружать данные в поля кода и названия, которые являются простыми текстовыми полями, но не могу выбрать соответствующую компанию и тип продукта в своем пользовательском селекторе. Он оказывается пустым.

Вот мой код для 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"

  1. Проблема обновления состояния: Убедитесь, что состояние 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]);
  2. Проблемы с передачей значений из 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. Убедитесь, что для каждой сущности вы работаете с актуальными значениями и контролируете состояние загрузки.

Оцените материал
Добавить комментарий

Капча загружается...