skip to Main Content

I have an issue which I’m trying to solve for some time:
how can I trigger this fetch: const { data: warrantyInfo } = warrantyApi.useGetWarrantyInfoQuery(materials[0].serialNumber); only when I type something on serialNumber inputs by using setTimeout(),1000?

Currently is triggering on page load and on every type that the user does in the serialNumber input.

I’ve tried some ways but doesn’t work because this is a hook and I can’t use it into another function or useEffect for example.

import { useState } from 'react';
import { toast } from 'react-toastify';
import { styled } from '@mui/material/styles';
import Dialog from '@mui/material/Dialog';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import DialogActions from '@mui/material/DialogActions';
import IconButton from '@mui/material/IconButton';
import { isBefore, parseISO } from 'date-fns';
import { ArrowLeft, ArrowRight, ArrowRightLong, CheckOutline, Close, Table, Trash, Upload } from 'assets/icons';
import { Checkbox, FormControlLabel, Grid, InputAdornment, MenuItem, Select, TextField } from '@mui/material';

const ModalAddC = (props) => {
    const { open, handleClose } = props;

    const { t } = useTranslation();

    const [failedSteps] = useState<Array<number>>([]);
    const [activeStep, setActiveStep] = useState<number>(0);
    const [fiveYearWarranty, setFiveYearWarranty] = useState<boolean>(false);
    const [detailedInformation, setDetailedInformation] = useState<string>('' as string);
    const [returnReason, setReturnReason] = useState<string>('' as string);
    const [materialInputType, setMaterialInputType] = useState<boolean>(true);
    const [materials, setMaterials] = useState<Array<{ serialNumber: string; quantity: number; materialDesignation: string }>>([
        { serialNumber: '', quantity: 0, materialDesignation: '' },
    ]);
    const [fileIsUploaded, setFileIsUploaded] = useState<boolean>(false);
    const [commissionName, setCommissionName] = useState<string>('' as string);
    const [billNumber, setBillNumber] = useState<string>('' as string);

    const { data: warrantyInfo } = warrantyApi.useGetWarrantyInfoQuery(materials[0].serialNumber);

    const handleNavigation = (step: number) => {
        if (step < activeStep) {
            setActiveStep(step);
        }
    };

    const handleNext = () => {
        let errors = 0;
        if(!commissionName.length){
            errors = displayError(t('Commission name is required.'));
        }
        if(!billNumber.length){
            errors = displayError(t('Bill number is required.'));
        }
        if(!detailedInformation.length){
            errors = displayError(t('Detailed information is required.'));
        }
        if(!returnReason.length){
            errors = displayError(t('Return reason is required.'));
        }
        if(errors){
            return;
        }
        setActiveStep((current) => current + 1);
    };

    const handleSave = () => {
        let errors = 0;
        materials.forEach((material) => {
            if(!material.serialNumber.length){
                errors = displayError(t('Serial Number is required.'));
            }
            if(!material.quantity){
                errors = displayError(t('Quantity is required.'));
            }
            if(!material.materialDesignation.length){
                errors = displayError(t('Material Designation is required.'));
            }
        })
        if(errors){
            return;
        }
        handleClose();
    };

    const handleDecreaseAmount = (index: number) => {
        if (materials[index].quantity > 0) {
            setMaterials((current) => {
                const newMaterials = [...current];
                newMaterials[index].quantity = newMaterials[index].quantity - 1;
                return newMaterials;
            });
        }
    };
    const handleIncreaseAmount = (index: number) => {
        setMaterials((current) => {
            const newMaterials = [...current];
            newMaterials[index].quantity = newMaterials[index].quantity + 1;
            return newMaterials;
        });
    };
    const handleQuantityChange = (index: number, newValue: number) => {
        setMaterials((current) => {
            const newMaterials = [...current];
            newMaterials[index].quantity = newValue;
            return newMaterials;
        });
    };
    const handleSerialNumberChange = (index: number, newValue: string) => {
        setMaterials((current) => {
            const newMaterials = [...current];
            newMaterials[index].serialNumber = newValue;
            return newMaterials;
        });
    };
    const handleMaterialDesignationChange = (index: number, newValue: string) => {
        setMaterials((current) => {
            const newMaterials = [...current];
            newMaterials[index].materialDesignation = newValue;
            return newMaterials;
        });
    };

    const handleCreateNewMaterial = () => {
        setMaterials((current) => [...current, { serialNumber: '', quantity: 0, materialDesignation: '' }]);
    };

    const handleDeleteMaterial = (index: number) => {
        if (materials.length > 1) {
            setMaterials((current) => {
                const newMaterials = [...current];
                newMaterials.splice(index, 1);
                return newMaterials;
            });
        }
    };

    const handleSubmitCommission = () => {
        const isWarrantyInfoInPast = warrantyInfo
        ? isBefore(parseISO(warrantyInfo?.end_date), new Date())
        : false;
        if(isWarrantyInfoInPast) {
            toast.error(t('According to our record, the warranty has expired! Feel free to contact us with any questions!'));
        } 
        handleSave();
    };

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const readUploadFile = (e: any) => {
        e.preventDefault();
        if (e.target.files) {
            const reader = new FileReader();
            reader.onload = (e) => {
                const data = e.target!.result;
                const workbook = xlsx.read(data, { type: 'array' });
                const sheetName = workbook.SheetNames[0];
                const worksheet = workbook.Sheets[sheetName];
                const json = xlsx.utils.sheet_to_json(worksheet);
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                const newArray = json.map((item: any) => ({
                    serialNumber: item['Material Nummer'],
                    quantity: item['Anazhl'],
                    materialDesignation: '',
                }));
                setMaterials(newArray);
                setFileIsUploaded(true);
            };
            reader.readAsArrayBuffer(e.target.files[0]);
        }
    };

    const openFileInput = () => {
        document.getElementById('hiddenFileInput')!.click();
    };

    return (
        <BootstrapDialog onClose={handleClose} aria-labelledby="customized-dialog-title" open={open} className="rp-modal-add-commission">
            <BootstrapDialogTitle id="customized-dialog-title" onClose={handleClose}>
                <ProgressBar
                    steps={[t('Commission'), t('Material')]}
                    failedSteps={failedSteps}
                    activeStep={activeStep}
                    setActiveStep={(step) => handleNavigation(step)}
                />
            </BootstrapDialogTitle>
            <DialogContent dividers>
                <div className="slider" style={{ transform: `translateX(calc(-${activeStep * 80}vw + ${activeStep * 6}px))` }}>
                    <div className="step">
                        <span className="section-title">{t('Add commission')}</span>
                        <Grid container spacing={0}>
                            <Grid item className="custom-grid" xs={6} md={6} lg={6}>
                                <br></br>
                                <span className="input-label">{t('Commission name').toString() + '*'}</span>
                                <TextField
                                    id="outlined-textfield"
                                    label=""
                                    variant="outlined"
                                    className="custom-text-field"
                                    InputLabelProps={{ shrink: false }}
                                    value={commissionName}
                                    onChange={(e) => setCommissionName(e.target.value)}
                                    error={!commissionName.length}
                                    helperText={!commissionName.length && t('Commission name is required.').toString()}
                                />
                                <FormControlLabel
                                    control={
                                        <Checkbox
                                            color="secondary"
                                            checked={fiveYearWarranty}
                                            onChange={() => setFiveYearWarranty(!fiveYearWarranty)}
                                        />
                                    }
                                    label={`${t('The product has an extended warranty')}(${t('5-years warranty')})`}
                                />
                                <Grid container spacing={0}>
                                    <Grid item className="custom-inside-grid" xs={6} md={6} lg={6}>
                                        <span className="input-label">{t('Serial Number').toString() + (fiveYearWarranty ? '*' : '')}</span>
                                        <TextField
                                            id="outlined-textfield"
                                            label=""
                                            variant="outlined"
                                            className="custom-text-field"
                                            InputLabelProps={{ shrink: false }}
                                        />
                                    </Grid>
                                    <Grid item className="custom-inside-grid" xs={6} md={6} lg={6}>
                                        <span className="input-label">{t('Bill Number').toString() + '*'}</span>
                                        <TextField
                                            id="outlined-textfield"
                                            label=""
                                            variant="outlined"
                                            className="custom-text-field"
                                            InputLabelProps={{ shrink: false }}
                                            value={billNumber}
                                            onChange={(e) => setBillNumber(e.target.value)}
                                            error={!billNumber.length}
                                            helperText={!billNumber.length && t('Bill number is required.').toString()}
                                        />
                                    </Grid>
                                </Grid>
                                <FormControlLabel control={<Checkbox color="secondary" />} label={t('Replacement of this position is desired')} />
                                <span className="input-label">{t('Comment').toString()}</span>
                                <TextField
                                    id="outlined-textfield"
                                    label=""
                                    variant="outlined"
                                    className="custom-text-field"
                                    InputLabelProps={{ shrink: false }}
                                    multiline
                                    rows={2}
                                />
                            </Grid>
                            <Grid item className="custom-grid" xs={6} md={6} lg={6}>
                                <br></br>
                                <span className="input-label">{t('Detailed information').toString() + '*'}</span>
                                <Select
                                    value={detailedInformation}
                                    label=""
                                    onChange={(e) => setDetailedInformation(e.target.value as string)}
                                    color="secondary"
                                    error={!detailedInformation.length}
                                    displayEmpty>
                                    <MenuItem value="" disabled>
                                        <span className="placeholder">{t('Please select').toString()}</span>
                                    </MenuItem>
                                    <MenuItem value={'1'}>
                                        <span className="menu-option">{t('Too many products')}</span>
                                    </MenuItem>
                                    <MenuItem value={'2'}>
                                        <span className="menu-option">{t('Incorrect order')}</span>
                                    </MenuItem>
                                    <MenuItem value={'3'}>
                                        <span className="menu-option">{t('Customer cancelled order')}</span>
                                    </MenuItem>
                                    <MenuItem value={'4'}>
                                        <span className="menu-option">{t('Inventory cleanup')}</span>
                                    </MenuItem>
                                    <MenuItem value={'5'}>
                                        <span className="menu-option">{t('Delivery time')}</span>
                                    </MenuItem>
                                    <MenuItem value={'6'}>
                                        <span className="menu-option">{t('Delivery time too long')}</span>
                                    </MenuItem>
                                    <MenuItem value={'7'}>
                                        <span className="menu-option">{t('Products too expensive / incorrect price')}</span>
                                    </MenuItem>
                                </Select>
                                {!detailedInformation.length && <span className='detailed-information-err-text'>{t('Detailed information is required.')}</span>}
                                <br></br>
                                <span className="input-label">{t('Reason of return').toString() + '*'}</span>
                                <Select
                                    value={returnReason}
                                    label=""
                                    onChange={(e) => setReturnReason(e.target.value as string)}
                                    color="secondary"
                                    error={!returnReason.length}
                                    displayEmpty>
                                    <MenuItem value="" disabled>
                                        <span className="placeholder">{t('Please select').toString()}</span>
                                    </MenuItem>
                                    <MenuItem value={'1'}>
                                        <span className="menu-option">{t('Like new, not needed by customer')}</span>
                                    </MenuItem>
                                    <MenuItem value={'2'}>
                                        <span className="menu-option">{t('Defective')}</span>
                                    </MenuItem>
                                    <MenuItem value={'3'}>
                                        <span className="menu-option">{t('Transport damage')}</span>
                                    </MenuItem>
                                    <MenuItem value={'4'}>
                                        <span className="menu-option">{t('Wrong delivery')}</span>
                                    </MenuItem>
                                    <MenuItem value={'5'}>
                                        <span className="menu-option">{t('Spare parts inventory reconciliation upon consultation')}</span>
                                    </MenuItem>
                                </Select>
                                {!returnReason.length && <span className='return-reason-err-text'>{t('Return reason is required.')}</span>}
                                <br></br>
                                <span className="input-label">{t('Pictures/files for return').toString()}</span>
                                <div className="upload-container">
                                    <Upload className="upload-icon" />
                                    <span className="upload-main-text">{t('Drag or select files here')}</span>
                                    <span className="upload-secondary-text">{t('max. 10MB in  PDF, JPG, PNG format')}</span>
                                </div>
                            </Grid>
                        </Grid>
                    </div>
                    <div className="step">
                        <span className="section-title">{t('Add Material')}</span>
                        <Grid container spacing={0}>
                            <Grid item className="custom-grid" xs={12} md={12} lg={12}>
                                <Toggle
                                    items={[<>{t('Manual input')}</>, <>{t('Excel upload')}</>]}
                                    setToggle={(e) => {
                                        setMaterialInputType(e);
                                        setMaterials([{ serialNumber: '', quantity: 0, materialDesignation: '' }]);
                                        setFileIsUploaded(false);
                                    }}
                                    value={materialInputType}
                                />
                            </Grid>
                        </Grid>
                        {materialInputType ? (
                            <>
                                {materials.map((material, index) => (
                                    <Grid key={'materrial-' + index} container spacing={0}>
                                        <Grid item className="custom-grid" xs={4} md={4} lg={4}>
                                            <span className="input-label">{t('Serial Number').toString()}*</span>
                                            <TextField
                                                id="outlined-textfield"
                                                label=""
                                                variant="outlined"
                                                className="custom-text-field"
                                                InputLabelProps={{ shrink: false }}
                                                value={material.serialNumber}
                                                onChange={(e) => handleSerialNumberChange(index, e.target.value)}
                                                error={!material.serialNumber.length}
                                                helperText={!material.serialNumber.length && t('Serial Number is required.').toString()}
                                            />
                                        </Grid>
                                        <Grid item className="custom-grid" xs={3} md={3} lg={3}>
                                            <span className="input-label">{t('Quantity').toString() + '*'}</span>
                                            <TextField
                                                type="number"
                                                value={material.quantity}
                                                error={!material.quantity}
                                                // onChange={(e) => setPackagestValue(parseInt(e.target.value))}
                                                onChange={(e) => handleQuantityChange(index, parseInt(e.target.value))}
                                                InputProps={{
                                                    startAdornment: (
                                                        <InputAdornment position="start">
                                                            <IconButton onClick={() => handleDecreaseAmount(index)}>
                                                                <ArrowLeft />
                                                            </IconButton>
                                                        </InputAdornment>
                                                    ),
                                                    endAdornment: (
                                                        <InputAdornment position="end">
                                                            <IconButton onClick={() => handleIncreaseAmount(index)}>
                                                                <ArrowRight />
                                                            </IconButton>
                                                        </InputAdornment>
                                                    ),
                                                }}
                                            />
                                            {!material.quantity && <span className='material-qty-err-text'>{t('Quantity is required.')}</span>}
                                        </Grid>
                                        <Grid item className="custom-grid" xs={4} md={4} lg={4}>
                                            <span className="input-label">{t('Material designation').toString()}*</span>
                                            <TextField
                                                id="outlined-textfield"
                                                label=""
                                                variant="outlined"
                                                className="custom-text-field"
                                                InputLabelProps={{ shrink: false }}
                                                value={material.materialDesignation}
                                                onChange={(e) => handleMaterialDesignationChange(index, e.target.value)}
                                                error={!material.materialDesignation.length}
                                                helperText={!material.materialDesignation.length && t('Material Designation is required.').toString()}
                                            />
                                        </Grid>
                                        <Grid item className="custom-grid justify-center" xs={1} md={1} lg={1}>
                                            <Trash className="cursor-pointer" onClick={() => handleDeleteMaterial(index)} />
                                        </Grid>
                                    </Grid>
                                ))}
                                <Grid container>
                                    <Grid item xs={12} md={12} lg={12}>
                                        <button className="add-material-button" onClick={() => handleCreateNewMaterial()}>
                                            {t('Add more materials')}
                                        </button>
                                    </Grid>
                                </Grid>
                            </>
                        ) : (
                            <>
                                {!fileIsUploaded ? (
                                    <>
                                        {materials.map((material, index) => (
                                            <Grid key={'materrial-' + index} container spacing={0}>
                                                <Grid item className="custom-grid" xs={4} md={4} lg={4}>
                                                    <span className="input-label">{t('Serial Number').toString()}*</span>
                                                    <TextField
                                                        id="outlined-textfield"
                                                        label=""
                                                        variant="outlined"
                                                        className="custom-text-field"
                                                        InputLabelProps={{ shrink: false }}
                                                        value={material.serialNumber}
                                                        onChange={(e) => handleSerialNumberChange(index, e.target.value)}
                                                    />
                                                </Grid>
                                                <Grid item className="custom-grid" xs={3} md={3} lg={3}>
                                                    <span className="input-label">{t('Quantity').toString() + '*'}</span>
                                                    <TextField
                                                        type="number"
                                                        value={material.quantity}
                                                        // onChange={(e) => setPackagestValue(parseInt(e.target.value))}
                                                        onChange={(e) => handleQuantityChange(index, parseInt(e.target.value))}
                                                        InputProps={{
                                                            startAdornment: (
                                                                <InputAdornment position="start">
                                                                    <IconButton onClick={() => handleDecreaseAmount(index)}>
                                                                        <ArrowLeft />
                                                                    </IconButton>
                                                                </InputAdornment>
                                                            ),
                                                            endAdornment: (
                                                                <InputAdornment position="end">
                                                                    <IconButton onClick={() => handleIncreaseAmount(index)}>
                                                                        <ArrowRight />
                                                                    </IconButton>
                                                                </InputAdornment>
                                                            ),
                                                        }}
                                                    />
                                                </Grid>
                                                <Grid item className="custom-grid" xs={4} md={4} lg={4}>
                                                    <span className="input-label">{t('Material designation').toString()}*</span>
                                                    <TextField
                                                        id="outlined-textfield"
                                                        label=""
                                                        variant="outlined"
                                                        className="custom-text-field"
                                                        InputLabelProps={{ shrink: false }}
                                                        value={material.materialDesignation}
                                                        onChange={(e) => handleMaterialDesignationChange(index, e.target.value)}
                                                    />
                                                </Grid>
                                                <Grid item className="custom-grid justify-center" xs={1} md={1} lg={1}>
                                                    <Trash className="cursor-pointer" onClick={() => handleDeleteMaterial(index)} />
                                                </Grid>
                                            </Grid>
                                        ))}
                                        <Grid container>
                                            <Grid item xs={12} md={12} lg={12}>
                                                <button className="add-material-button" onClick={() => handleCreateNewMaterial()}>
                                                    {t('Add more materials')}
                                                </button>
                                                <button
                                                    className="add-material-button"
                                                    onClick={() => {
                                                        setFileIsUploaded(false);
                                                        setMaterials([{ serialNumber: '', quantity: 0, materialDesignation: '' }]);
                                                    }}>
                                                    {t('Replace excel file')}
                                                </button>
                                            </Grid>
                                        </Grid>
                                    </>
                                ) : (
                                    <>
                                        <Grid container spacing={0}>
                                            <Grid item className="custom-grid" xs={4} md={4} lg={4}>
                                                <Table />
                                                <br></br>
                                                <span className="excel-upload-description">
                                                    {t('Create your Excel table in the form shown here, or simply use our Excel template.')}
                                                </span>
                                                <br></br>
                                                <button className="add-material-button download-excel-template-button">
                                                    {t('Download Excel template')}
                                                </button>
                                            </Grid>
                                            <Grid item xs={8} md={8} lg={8}>
                                                <div className="upload-container">
                                                    <Upload className="upload-icon" onClick={openFileInput} />
                                                    <span className="upload-main-text">{t('Drag or select files here')}</span>
                                                    <span className="upload-secondary-text">{t('max. 10MB in  xls, csv format')}</span>
                                                </div>
                                                <input
                                                    type="file"
                                                    id="hiddenFileInput"
                                                    style={{ display: 'none' }}
                                                    onChange={readUploadFile}
                                                    accept=".xls, .csv, .xlsx"
                                                />
                                            </Grid>
                                        </Grid>
                                    </>
                                )}
                            </>
                        )}
                        <Grid container spacing={0}>
                            <Grid item className="custom-grid" xs={12} md={12} lg={12}></Grid>
                        </Grid>
                    </div>
                </div>
            </DialogContent>
            <DialogActions>
                {activeStep === 0 && (
                    <>
                        <button className="next-step-btn-negative" onClick={() => handleNext()}>
                            {t('Create and add material later')}
                        </button>
                        <button className="next-step-btn" onClick={() => handleNext()}>
                            {t('Next step')}
                            <ArrowRightLong className="icon" />
                        </button>
                    </>
                )}
                {activeStep === 1 && (
                    <button className="next-step-btn" onClick={() => handleSubmitCommission()}>
                        <CheckOutline className="icon-before" />
                        {t('Create commission')}
                    </button>
                )}
            </DialogActions>
        </BootstrapDialog>
    );
};

export default ModalAddC;

2

Answers


  1. Try this technique.

    import { useEffect, useState } from 'react';
    
    function YourComponent() {
      const [serialNumber, setSerialNumber] = useState('');
      const [debouncedSerialNumber, setDebouncedSerialNumber] = useState('');
      const { data: warrantyInfo } = warrantyApi.useGetWarrantyInfoQuery(debouncedSerialNumber);
    
      // Update debouncedSerialNumber after a delay
      useEffect(() => {
        const timeoutId = setTimeout(() => {
          setDebouncedSerialNumber(serialNumber);
        }, 1000);
    
        // Clear the previous timeout when the user types more
        return () => {
          clearTimeout(timeoutId);
        };
      }, [serialNumber]);
    
      // Handle input change
      const handleInputChange = (e) => {
        setSerialNumber(e.target.value);
      };
    
      return (
        <div>
          <input
            type="text"
            value={serialNumber}
            onChange={handleInputChange}
            placeholder="Serial Number"
          />
          {/* Render your warrantyInfo data here */}
        </div>
      );
    }
    
    Login or Signup to reply.
  2. The regular query hooks are intended to be called each time the component renders, e.g. from the state update, and the argument is passed to the underlying query endpoint. If you are wanting a bit more control over when the query is triggered then I’d suggest using the lazy query hook and debounce the returned trigger function.

    useLazyQuery

    Example implementation:

    interface Material {
      serialNumber: string;
      quantity: number;
      materialDesignation: string
    }
    
    const [materials, setMaterials] = useState<Array<Material>>(
      [{ serialNumber: '', quantity: 0, materialDesignation: '' }]
    );
    
    const [
      trigger,
      { data: warrantyInfo }
    ] = warrantyApi.useLazyGetWarrantyInfoQuery();
    
    ...
    

    Use a useEffect hook to call a debounced trigger function.

    • You can import a debounce Higher Order Function from a library like lodash

      import { debounce } from 'lodash';
      
    • You can implement your own simple debounce function

      const debounce = (fn, delay) => {
        let timerId;
        return (...args) => {
          clearTimeout(timerId);
          timerId = setTimeout(() => fn(...args), delay);
        }
      };
      
    const debouncedTrigger = React.useCallback(
      debounce(trigger, 1000),
      [trigger]
    );
    
    useEffect(() => {
      if (materials[0].serialNumber) {
        // Only call trigger if serial number is truthy
        debouncedTrigger(materials[0].serialNumber);
      }
    }, [debouncedTrigger, materials]);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search