Functional component state losing values after upgrading it: "Cannot read Property ..."

Asked

Viewed 62 times

-1

I have a problem with a React application. Whenever I activate the onBlur={}, error occurs TypeError: Cannot read property 'valido' of undefined'. How can I solve?

Code of Signup.jsx clients

import React, { useState } from "react";
import "./CadastroClientes.css";
import {
  Button,
  TextField
} from "@material-ui/core";

function CadastroClientes({ enviarCliente, validarNomeEmpresa, validarNomePonto }) {

    const data = ['Ativo', 'Inativo']

  const [nomeEmpresa, setNomeEmpresa] = useState("");
  const [nomePonto, setNomePonto] = useState("");
  const [codigo, setCodigo] = useState("");
  const [descricaoSICP, setDescricaoSICP] = useState("");
  const [status, setStatus] = useState("Ativo");
  const [pais, setPais] = useState("");
  const [estado, setEstado] = useState("");
  const [cidade, setCidade] = useState("");
  const [bairro, setBairro] = useState("");
  const [endereco, setEndereco] = useState("");
  const [lat, setLat] = useState("");
  const [long, setLong] = useState("");
  const [erros, setErros] = useState({nomeEmpresa: {valido: true, texto: ""}, nomePonto: {valido: false, texto: ""}, codigo: {valido: true, texto: ""}, descricaoSICP: {valido: true, texto: ""}, estado: {valido: true, texto: ""}, cidade: {valido: true, texto: ""}, bairro: {valido: true, texto: ""}, endereco: {valido: true, texto: ""}, lat: {valido: true, texto: ""}, long: {valido: true, texto: ""}});

  return (
    <div className="container">
      <form
        onSubmit={(event) => {
            enviarCliente({nomeEmpresa, nomePonto, codigo, descricaoSICP, status, pais, estado, cidade, bairro, endereco, lat, long});
            event.preventDefault();
        }}
      >
        <h1>Cadastro de Clientes</h1>
        <TextField
          value={nomeEmpresa}
          onBlur={(event) => {
              const ehValido = validarNomeEmpresa(nomeEmpresa);
              setErros({nomeEmpresa: ehValido});
          }}
          onChange={(event) => {
            setNomeEmpresa(event.target.value);
          }}
          color="primary"
          variant="outlined"
          margin="normal"
          id="nomeEmpresa"
          error={!erros.nomeEmpresa.valido}
          helperText={erros.nomeEmpresa.texto}
          label='Nome Empresa'
          fullWidth
        />
        <div className="flex">
          <TextField
            value={nomePonto}
            onBlur={(event) => {
              const isValid = validarNomePonto(nomePonto);
              setErros({nomePonto: isValid});
            }}
            onChange={(event) => {
              setNomePonto(event.target.value);
            }}
            color="primary"
            variant="outlined"
            margin="normal"
            id="nomePonto"
            error={!erros.nomePonto.valido}
            helperText={erros.nomePonto.texto}
            label="Nome do Ponto"
            className="w50"
          />
          <TextField
            value={codigo}
            onChange={(event) => {
              setCodigo(event.target.value);
            }}
            color="primary"
            variant="outlined"
            margin="normal"
            id="codigo"
            label="Código"
            className="w50"
          />
        </div>
        <TextField
          value={descricaoSICP}
          onChange={(event) => {
            setDescricaoSICP(event.target.value);
          }}
          color="primary"
          variant="outlined"
          margin="normal"
          id="descricaoSICP"
          label="Descrição SICP"
          fullWidth
        />

        <label for="exampleFormControlSelect1">Status</label>
        <select value={status} onChange={(event) => {
            setStatus(event.target.value)
        }} class="form-control" id="exampleFormControlSelect1">
            {
                data.map(i => (
                    <option>{i}</option>
                ))
            }
        </select>
        {/* Localização */}

        <h2>Localização</h2>

        <TextField
          value={pais}
          onChange={(event) => {
            setPais(event.target.value);
          }}
          color="primary"
          variant="outlined"
          margin="normal"
          id="pais"
          label="País"
          fullWidth
        />

        <div className="flex">
          <TextField
            value={estado}
            onChange={(event) => {
              setEstado(event.target.value);
            }}
            color="primary"
            variant="outlined"
            margin="normal"
            id="estado"
            label="Estado"
            className="w50"
          />
          <TextField
            value={cidade}
            onChange={(event) => {
              setCidade(event.target.value);
            }}
            color="primary"
            variant="outlined"
            margin="normal"
            id="cidade"
            label="Cidade"
            className="w50"
          />
        </div>
        <TextField
          value={bairro}
          onChange={(event) => {
            setBairro(event.target.value);
          }}
          color="primary"
          variant="outlined"
          margin="normal"
          id="bairro"
          label="Bairro"
          fullWidth
        />
        <TextField
          value={endereco}
          onChange={(event) => {
            setEndereco(event.target.value);
          }}
          color="primary"
          variant="outlined"
          margin="normal"
          id="endereco"
          label="Endereço"
          fullWidth
        />
        <div className="flex">
          <TextField
            value={lat}
            onChange={(event) => {
              setLat(event.target.value);
            }}
            color="primary"
            variant="outlined"
            margin="normal"
            id="lat"
            label="Latitude"
            className="w50"
          />
          <TextField
            value={long}
            onChange={(event) => {
              setLong(event.target.value);
            }}
            color="primary"
            variant="outlined"
            margin="normal"
            id="long"
            label="Longitude"
            className="w50"
          />
        </div>
        <Button type="submit" id="buttonCadastro" variant="contained" color="primary">Cadastrar Cliente</Button>
      </form>
    </div>
  );
}

export default CadastroClientes;

1 answer

1

The useState of a functional component does not work in the same way as the setState of a class component.

Citing the documentation on Componentes Classe (setState):

When you call setState(), React merges the object you provide to the current state.

Citing the documentation on Functional Components (useState):

(...) other than this.setState in class, when updating a state variable, it is always replaced instead of embedded.

Like the useState does not merge the current state with the new value, you must do this on your own. In the example below I make use of the Operator spread to facilitate this merging, in addition to correcting the object nomeEmpresa, that at state startup you declared nomeEmpresa: {valido: true, texto: ""} but ended up trying to replace by nomeEmpresa: ehValido in the onBlur.

setErros({ ...erros, nomeEmpresa: { ...erros.nomeEmpresa, valido: ehValido } });

This causes the old state to be copied to the new state, but with a new value for valido.


Below is an example of the use of useState in which it is possible to verify the standard behavior and behavior with the Operator spread.

function App() {
  const [data, setData] = React.useState({ alpha: 1, beta: 2 });

  function setOriginalState() {
    setData({ alpha: 1, beta: 2 });
  }

  function updateState() {
    setData({ charlie: 3 });
  }

  function updateStateSpread() {
    setData({ ...data, delta: 4 });
  }

  return (
    <div>
      <h1>Usando useState</h1>
      <h2>Estado: {JSON.stringify(data)}</h2>
      <button onClick={setOriginalState}>Retornar ao estado original</button>
      <br />
      <br />
      <button onClick={updateState}>
        Mudar estado com setData({"{ charlie: 3 }"})
      </button>
      <br />
      <br />
      <button onClick={updateStateSpread}>
        Mudar estado com setData({"{ ...data, delta: 4 }"})
      </button>
    </div>
  );
}

ReactDOM.render( <App /> , document.querySelector("#app"));
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

<div id="app"></div>

  • 1

    strange response, because useState also has how to merge! or not as you said? only if I got it wrong, and setState and this.seState are not the same thing? I can do the same thing in both ways both Component as function Component ... ! I really didn’t understand your answer.

  • @novic edited the answer to make more clear what he meant. The useState and setState have different behaviors as explained in the answer, in the documentation and as can be verified in this example which I have just created. It is possible to merge yes, as I did with the Operator spread, but it’s not standard behavior.

  • Thanks for the reply! But I still don’t understand what has to be changed in my code.

  • @Jaburu Seus setErros within the onBlur... They need to merge the old state with the new state (as in the answer code) rather than just replacing the state (as in their code). Emphasis on ...erros

  • I did it and it didn’t hurt ☹

  • @Jaburu you were modifying the structure of the object nomeEmpresa, in addition to the previously mentioned status override error, however I had not noticed it. I fixed the code now, take a look... It is necessary to maintain the same structure as the initial one (with texto and valido) instead of replacing this object with a Boolean, like. is in your code (nomeEmpresa: ehValido). Pay attention to others too onBlur to respect the correct structure defined in useState

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.