3

I am trying to have a single onChange event for each element that is rendered with the map function. The problem is that for each letter that I write, it is as if the execution stops, preventing me from writing freely and seeing the changes I am making in the text field in the console.

Here you can see the error: https://codesandbox.io/s/tender-wu-kuzqd

const options = [
  { id: 1, hour: 0, minute: 0, enrollment: '', value: 'ac.terapeutico', label: 'Ac. Terapéutico', color: getRandomColor() },
  { id: 2, hour: 0, minute: 0, enrollment: '', value: 'kinesiologia', label: 'Kinesiología', color: getRandomColor() },
  { id: 3, hour: 0, minute: 0, enrollment: '', value: 'oncologia', label: 'Oncología', color: getRandomColor() }
];

const Form = () => {
  const [selectedSpeciality, setSelectedSpeciality] = useState(null);
  const handleChangeEnrollment = (value, id) => {
    let index = selectedSpeciality.findIndex(el => el.id === id);
    const data = [...selectedSpeciality];
    data[index].enrollment = value;
    setSelectedSpeciality(data);
  }
  return (
    <Grid container spacing={2}>
      <Grid item xs={12}>
        <Select
          closeMenuOnSelect={true}
          isMulti
          options={options}
          styles={colourStyles}
          value={selectedSpeciality}
          onChange={setSelectedSpeciality}
          placeholder='Seleccione especialidades'
          noOptionsMessage={() => "No hay más opciones"}
        />
      </Grid>
      {selectedSpeciality &&
        selectedSpeciality.map(el => (
          <Fragment key={uuid()}>
            <Grid item xs={6}>
              <TextField
                fullWidth
                InputLabelProps={{
                  shrink: true,
                }}
                value={el.enrollment}
                onChange={(e) => handleChangeEnrollment(e.target.value, el.id)}
                label={`MATRÍCULA ${el.label.toUpperCase()}`}
              />
            </Grid>
          </Fragment>
        ))
      }
      {console.log(selectedSpeciality)}
    </Grid>
  );
}
John J.
  • 33
  • 4
  • Have you thought about using a [debounce](https://stackoverflow.com/questions/23123138/perform-debounce-in-react-js)? – Tejogol Oct 05 '20 at 17:48
  • @Tejogol I'm not sure I have to use debounce. They are simple text fields – John J. Oct 05 '20 at 17:54
  • @ChristianFritz el.enrollment is defined in the options array, and they are the options that the Select shows. As it is a multiple selector, for each option chosen, the entire element of the array is saved in selectedSpeciality. Finally, I make a map function that generates a text field, which has the function handleChangeEnrollment, which updates the value of the enrollment – John J. Oct 05 '20 at 18:15
  • There's a lot that's not shown here: the TextField component in particular. That's the first place I'd look for the cause of the delay. If you're able to create a replica of the problem, perhaps on Stackblitz, that would make it easier to assist you. – Richard Hunter Oct 05 '20 at 18:45
  • Can you mention the frontend library that you used for your code? Because I migrate your code to default HTML components and it works fine. – Vikum Dheemantha Oct 05 '20 at 19:02
  • @RichardHunter Here I leave the link to see the error. When you select any option from the selector, when writing in the field it allows you one letter, and it does not allow you to write freely https://codesandbox.io/s/tender-wu-kuzqd – John J. Oct 05 '20 at 19:46
  • @VikumDheemantha Here I leave the link to see the error. When you select any option from the selector, when writing in the field it allows you one letter, and it does not allow you to write freely https://codesandbox.io/s/tender-wu-kuzqd – John J. Oct 05 '20 at 19:47

1 Answers1

2

The problem is that you are using Fragment with uuid, which create a new instance at each render.
This results in rendering the whole part again and losing focus.

You need to remove Fragment or use any other consistent key value for each element in map.

Without Fragment

See: https://codesandbox.io/s/lucid-field-crolh?file=/src/App.js

import React, { useState, Fragment } from "react";
import uuid from "react-uuid";
import styled from "styled-components";
import Select from "react-select";

import { Grid, TextField, Box } from "@material-ui/core";

export const StyleWrapper = styled.div`
  .MuiInputBase-root {
    font-size: 24px;
    font-weight: 600;
    color: #061655;
  }
`;

const dot = (color = "#ccc") => ({
  alignItems: "center",
  display: "flex",

  ":before": {
    backgroundColor: color,
    borderRadius: 10,
    content: '" "',
    display: "block",
    marginRight: 4,
    marginLeft: 8,
    height: 10,
    width: 10
  }
});

const colourStyles = {
  control: (styles) => ({ ...styles, backgroundColor: "white" }),
  option: (styles, { data, isDisabled, isFocused, isSelected }) => {
    return {
      ...styles,
      backgroundColor: isDisabled
        ? null
        : isSelected
        ? data.color
        : isFocused
        ? "000000"
        : null,
      color: isDisabled
        ? "#ccc"
        : isSelected
        ? "000000" > 2
          ? "white"
          : "black"
        : "000000",
      cursor: isDisabled ? "not-allowed" : "default",

      ":active": {
        ...styles[":active"],
        backgroundColor: !isDisabled && (isSelected ? "000000" : "000000")
      },
      borderRadius: "10px"
    };
  },
  placeholder: (styles) => ({ ...styles, ...dot() }),
  multiValue: (styles, { data }) => ({
    ...styles,
    ...dot(data.color),
    borderRadius: "40px"
  }),
  multiValueLabel: (styles) => ({
    ...styles,
    color: "#061655",
    fontWeight: 500
  }),
  multiValueRemove: (styles, { data }) => ({
    ...styles,
    color: "#061655",
    padding: "8px 8px 8px 4px",
    ":hover": {
      backgroundColor: "#transparent",
      color: "#061655",
      cursor: "pointer",
      borderRadius: "0px 40px 40px 0px"
    }
  })
};

const FormAddProfessional = (props) => {
  const [selectedSpeciality, setSelectedSpeciality] = useState(null);

  const optionsSpeciality = [
    {
      id: 1,
      hour: 0,
      minute: 0,
      enrollment: "",
      value: "ac.terapeutico",
      label: "Ac. Terapéutico"
    },
    {
      id: 2,
      hour: 0,
      minute: 0,
      enrollment: "",
      value: "kinesiologia",
      label: "Kinesiología"
    },
    {
      id: 3,
      hour: 0,
      minute: 0,
      enrollment: "",
      value: "oncologia",
      label: "Oncología"
    }
  ];

  const handleChangeEnrollment = (value, id) => {
    let index = selectedSpeciality.findIndex((el) => el.id === id);
    const data = [...selectedSpeciality];
    data[index].enrollment = value;
    setSelectedSpeciality(data);
  };

  return (
    <div style={{ width: "100%" }}>
      <div style={{ paddingLeft: 4 }}>
        <Box borderLeft={1} borderColor="#EBEBEB" style={{ padding: 12 }}>
          <Grid container spacing={2}>
            <Grid item xs={12}>
              <Select
                closeMenuOnSelect={true}
                isMulti
                options={optionsSpeciality}
                styles={colourStyles}
                value={selectedSpeciality}
                onChange={setSelectedSpeciality}
                placeholder="Seleccione especialidades"
                noOptionsMessage={() => "No hay más opciones"}
              />
            </Grid>
            {selectedSpeciality &&
              selectedSpeciality.map((el) => (
                <Grid item xs={6}>
                  <TextField
                    fullWidth
                    InputLabelProps={{
                      shrink: true
                    }}
                    value={el.enrollment}
                    onChange={(e) =>
                      handleChangeEnrollment(e.target.value, el.id)
                    }
                    label={`MATRÍCULA ${el.label.toUpperCase()}`}
                  />
                </Grid>
              ))}
            {console.log(selectedSpeciality)}
          </Grid>
        </Box>
      </div>
    </div>
  );
};

export default FormAddProfessional;

With different consistent key value

See: https://codesandbox.io/s/angry-jang-8w004?file=/src/App.js:0-4014

import React, { useState, Fragment } from "react";
import uuid from "react-uuid";
import styled from "styled-components";
import Select from "react-select";

import { Grid, TextField, Box } from "@material-ui/core";

export const StyleWrapper = styled.div`
  .MuiInputBase-root {
    font-size: 24px;
    font-weight: 600;
    color: #061655;
  }
`;

const dot = (color = "#ccc") => ({
  alignItems: "center",
  display: "flex",

  ":before": {
    backgroundColor: color,
    borderRadius: 10,
    content: '" "',
    display: "block",
    marginRight: 4,
    marginLeft: 8,
    height: 10,
    width: 10
  }
});

const colourStyles = {
  control: (styles) => ({ ...styles, backgroundColor: "white" }),
  option: (styles, { data, isDisabled, isFocused, isSelected }) => {
    return {
      ...styles,
      backgroundColor: isDisabled
        ? null
        : isSelected
        ? data.color
        : isFocused
        ? "000000"
        : null,
      color: isDisabled
        ? "#ccc"
        : isSelected
        ? "000000" > 2
          ? "white"
          : "black"
        : "000000",
      cursor: isDisabled ? "not-allowed" : "default",

      ":active": {
        ...styles[":active"],
        backgroundColor: !isDisabled && (isSelected ? "000000" : "000000")
      },
      borderRadius: "10px"
    };
  },
  placeholder: (styles) => ({ ...styles, ...dot() }),
  multiValue: (styles, { data }) => ({
    ...styles,
    ...dot(data.color),
    borderRadius: "40px"
  }),
  multiValueLabel: (styles) => ({
    ...styles,
    color: "#061655",
    fontWeight: 500
  }),
  multiValueRemove: (styles, { data }) => ({
    ...styles,
    color: "#061655",
    padding: "8px 8px 8px 4px",
    ":hover": {
      backgroundColor: "#transparent",
      color: "#061655",
      cursor: "pointer",
      borderRadius: "0px 40px 40px 0px"
    }
  })
};

const FormAddProfessional = (props) => {
  const [selectedSpeciality, setSelectedSpeciality] = useState(null);

  const optionsSpeciality = [
    {
      id: 1,
      hour: 0,
      minute: 0,
      enrollment: "",
      value: "ac.terapeutico",
      label: "Ac. Terapéutico"
    },
    {
      id: 2,
      hour: 0,
      minute: 0,
      enrollment: "",
      value: "kinesiologia",
      label: "Kinesiología"
    },
    {
      id: 3,
      hour: 0,
      minute: 0,
      enrollment: "",
      value: "oncologia",
      label: "Oncología"
    }
  ];

  const handleChangeEnrollment = (value, id) => {
    let index = selectedSpeciality.findIndex((el) => el.id === id);
    const data = [...selectedSpeciality];
    data[index].enrollment = value;
    setSelectedSpeciality(data);
  };

  return (
    <div style={{ width: "100%" }}>
      <div style={{ paddingLeft: 4 }}>
        <Box borderLeft={1} borderColor="#EBEBEB" style={{ padding: 12 }}>
          <Grid container spacing={2}>
            <Grid item xs={12}>
              <Select
                closeMenuOnSelect={true}
                isMulti
                options={optionsSpeciality}
                styles={colourStyles}
                value={selectedSpeciality}
                onChange={setSelectedSpeciality}
                placeholder="Seleccione especialidades"
                noOptionsMessage={() => "No hay más opciones"}
              />
            </Grid>
            {selectedSpeciality &&
              selectedSpeciality.map((el) => (
                <Fragment key={el.value}>
                  <Grid item xs={6}>
                    <TextField
                      fullWidth
                      InputLabelProps={{
                        shrink: true
                      }}
                      value={el.enrollment}
                      onChange={(e) =>
                        handleChangeEnrollment(e.target.value, el.id)
                      }
                      label={`MATRÍCULA ${el.label.toUpperCase()}`}
                    />
                  </Grid>
                </Fragment>
              ))}
            {console.log(selectedSpeciality)}
          </Grid>
        </Box>
      </div>
    </div>
  );
};

export default FormAddProfessional;
Itamar
  • 1,601
  • 1
  • 10
  • 23