Home > Software engineering >  List Components not rendering properly after applying array.filter() ReactJS
List Components not rendering properly after applying array.filter() ReactJS

Time:09-03

I have the following problem, in my React screen when I add this array of products which would look like this

products = [ { "cantidadcajas": "1", "cajassurtidas": "", "fecha": "", "noproducto": "1", "descripcion": "aaa", "temperatura": "1", "kilosproducto": "1" }, { "cantidadcajas": "2", "cajassurtidas": "", "fecha": "", "noproducto": "2", "descripcion": "ss", "temperatura": "2", "kilosproducto": "2" } ]

screen wrong rendering

The goal is that when I click on the - sign button for the element is to remove that specific item.

But when I filter by index, the console.log() show the proper array to render, but screen renders something else. Here is the code I am using => https://codeshare.io/78QZeL

Here I paste also the code

import React, { useEffect, useState } from "react";
import { Box, Paper, Typography, Grid, Button, TextField } from "@mui/material";
import SelectInput from "../../Shared/SelectInput";
import { faker } from "@faker-js/faker";
import { TextInput } from "../../Shared/TextInput";
import { DateInput } from "../../Shared/DateInput";
import formatDate from "../../../utils/formatDate";
import { useForm } from "../../../hooks/useForm";
import { Clients as ClientsApi, ServicesApi, Pallets } from "../../../lib/api";
import SnackBar from "../../Shared/SnackBar";
export const Entry = () => {
  const [clients, setClients] = useState([]);
  const [client, setClient] = useState({
    _id: "",
    name: ""
  });
  const [disabledForm, setDisabledForm] = useState(false);
  const [entryDate, setEntryDate] = useState(null);
  const [formValues, handleInputChange] = useForm({
    idPallet: "",
    product: "",
    client: "",
    boxesNumber: "",
    kgNumber: "" //new Date().toLocaleString().split(",")[0]
  });
  const [products, setProducts] = useState([{
    cantidadcajas: "",
    cajassurtidas: "",
    fecha: "",
    noproducto: "",
    descripcion: "",
    temperatura: "",
    kilosproducto: ""
  }]);
  const snackBarRef = React.useRef(null);
  useEffect(() => {
    
  }, []);
  const sendInfo = () => {
    console.log("Printing products");
    console.log(products);
    let payload = {
      ID: formValues.idPallet,
      Product: formValues.product,
      Client: formValues.client,
      TotalBoxes: formValues.boxesNumber ? parseInt(formValues.boxesNumber) : 0,
      Weight: formValues.kgNumber ? parseFloat(formValues.kgNumber) : 0.0,
      EntryDate: entryDate,
    };
    console.log("Printing payload from entry pallets manual");
    console.log(payload);
    if (!payload.ID) {
      snackBarRef?.current.openSnackbar("error", "Porfavor ingresa el pallet e intentelo de nuevo.");
      return;
    }
    /*
    setDisabledForm(true);
    Pallets.postManualPallet(payload)
      .then(res => {
        console.log("Printing response in promise from post manual pallet");
        console.log(res);
        let severity = (res.status === 200) ? "success" : "error";
        let msg = (res.status === 200) ? "Pallet guardado exitosamente" : "Ha pasado un error";
        setDisabledForm(false);
        snackBarRef?.current.openSnackbar(severity, msg)
      })
      .catch(err => {
        console.log("In Catch blobk in Manual pallets post");
        console.log(err);
        setDisabledForm(false);
        snackBarRef?.current.openSnackbar("error", "Error desconocido, cheque logs");
      })*/
  };

  const addProduct = () => {
    const productsCopy = JSON.parse(JSON.stringify(products));
    const newProduct = {
      cantidadcajas: "",
      cajassurtidas: "",
      fecha: "",
      noproducto: "",
      descripcion: "",
      temperatura: "",
      kilosproducto: ""
    };

    productsCopy.push(newProduct);
    setProducts(productsCopy);
    
  };

  const removeProduct = (index) => {
    const productsCopy = JSON.parse(JSON.stringify(products));
    if (productsCopy.length > 1) {
      setProducts([]);
      console.log("Printing full products copy");
      console.log(productsCopy);
      const filteredProducts = productsCopy.filter((product, _index) => _index !== index);
      console.log("Printing filtered products");
      console.log(filteredProducts);
      setProducts(filteredProducts);
    }
  };

  const updateProduct = (e, index) => {
    const { name, value } = e.target;
    const productsCopy = JSON.parse(JSON.stringify(products)); 

    productsCopy[index][name] = value;
    setProducts(productsCopy);
  };

  return (
    <div
      style={{
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
      }}
    >
      <Paper
        elevation={3}
        sx={{
          maxWidth: "80%",
          alignSelf: "center",
          justifySelf: "center",
          flexGrow: 1,
          padding: ".5em .5em 3em .5em",
        }}
      >
        <Typography variant="pageTitle">Ingreso de Pallets - Manual</Typography>
        <Box sx={{ marginTop: "1em" }}>
          <Grid container spacing={2} sx={{ marginBottom: "1em" }}>
            <Grid item xs={12} sm={6} md={3} lg={3}>
              <TextInput
                label="ID del Pallet"
                name="idPallet"
                type="text"
                disabled={disabledForm}
                value={formValues.idPallet}
                onChange={handleInputChange}
              />
            </Grid>
            <Grid item xs={12} sm={6} md={3} lg={3}>
              <TextInput
                label="Cliente"
                name="client"
                type="text"
                disabled={disabledForm}
                value={formValues.client}
                onChange={handleInputChange}
              />
            </Grid>
            <Grid item xs={12} sm={6} md={3} lg={3}>
              <DateInput
                label={"Fecha de ingreso"}
                value={entryDate}
                disabled={disabledForm}
                onChange={(newValue) => setEntryDate(formatDate(newValue))}
              />
            </Grid>
            <br />
          </Grid>
          <Grid item xs={12} sm={6} md={3} lg={3}>
          {products.map((product, index) => {
            return (
              <div key={index} sx={{marginTop: "20px", marginBottom: "20px"}}>
                <TextInput
                  label="Numero de producto"
                  name="noproducto"
                  type="text"
                  disabled={disabledForm}
                  value={product.noproducto}
                  onChange={(e) => updateProduct(e, index)}
                />
                <TextInput
                  menuItems={clients}
                  label={"Descripcion"}
                  type="text"
                  name="descripcion"
                  disabled={disabledForm}
                  value={product.descripcion}
                  onChange={(e) => updateProduct(e, index)}
                />
                <TextInput
                  label="Cantidad cajas"
                  name="cantidadcajas"
                  type="number"
                  disabled={disabledForm}
                  value={product.cantidadcajas}
                  onChange={(e) => updateProduct(e, index)}
                />
                <TextInput
                  menuItems={clients}
                  label={"Peso/caja (kg)"}
                  type="number"
                  name="kilosproducto"
                  disabled={disabledForm}
                  value={product.kilosproducto}
                  onChange={(e) => updateProduct(e, index)}
                />
                <TextInput
                  menuItems={clients}
                  label={"Temperatura"}
                  type="number"
                  name="temperatura"
                  disabled={disabledForm}
                  value={product.temperatura}
                  onChange={(e) => updateProduct(e, index)}
                />
                <Button disabled={disabledForm} onClick={() => addProduct()} variant="contained">
                   
                </Button>
                <Button disabled={disabledForm} onClick={() => removeProduct(index)} variant="contained">
                  -
                </Button>
              </div>
            )
          })}
          </Grid>
          <Button sx={{marginTop: "15px"}} disabled={disabledForm} onClick={() => sendInfo()} variant="contained">
            Agregar Pallet
          </Button>
        </Box>
      </Paper>
      <SnackBar ref={snackBarRef} />
    </div>
  );
};

CodePudding user response:

Issue

The issue here is that you've chosen a poor React key for each mapped array element, the array index. The problem with using the array index is that the index value isn't attached to the element it represents. When you mutate the array and remove/insert/sort, the array indices remain the same. In other words, if you have an array of 3 elements and delete the second element, index 1 is still index 1. React sees that the React key didn't change and bails on rerendering. In all likelihood you probably only see the last array element removed (since the array has 1 element less).

Solution

Use a value intrinsic to the data that is mapped. id and other GUIDs are great candidates for React keys, but any property that is unique within the sibling elements is sufficient.

Example:

{products.map((product) => {
  return (
    <div key={product.id} sx={{ marginTop: "20px", marginBottom: "20px" }}>
      ...
    </div>
  )
})}

If there is no suitable property in the data to use as a React key then you will want to inject one. The uuid is a good package for generating GUIDs.

import { v4 as uuidV4 } from 'uuid';

...

const [products, setProducts] = useState([{
  id: uuidV4(),
  cantidadcajas: "",
  cajassurtidas: "",
  fecha: "",
  noproducto: "",
  descripcion: "",
  temperatura: "",
  kilosproducto: ""
}]);

...

const addProduct = () => {
  const newProduct = {
    id: uuidV4(),
    cantidadcajas: "",
    cajassurtidas: "",
    fecha: "",
    noproducto: "",
    descripcion: "",
    temperatura: "",
    kilosproducto: ""
  };

  setProducts(products => [...products, newProduct]);
};
  • Related