Onchange stops for every letter I type

Viewed 108

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>
  );
}
1 Answers

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;
Related