React useState objetos

Manejar useState con tipos elementales como string o number está muy bien, pero y si... ¿Tengo una ficha de cliente? ¿Tengo que crear un useState por cada campo? No, no te hace falta, directamente puedes manejar objetos, veamos como.

Actualizando objetos

Partiendo de un codesanbox de plantilla de React, vamos a añadir dos etiquetas y dos input para editar un nombre y un apellido de un cliente:

import "./styles.css";
import React from "react";

export default function App() {
  return (
    <div className="App">
      <h1>Hello Name Lastname</h1>
      <input />
      <input />
    </div>
  );
}

Vamos ahora a hacer uso de useState asignandole un objeto inicial:

import "./styles.css";
import React from 'react';

export default function App() {
+ const [myClient, setMyClient]  = React.useState({name: 'Pepe', lastname: 'Perez'});

  return (
    <div className="App">
      <h1>Hello Name Lastname</h1>
      <input/>
      <input/>
    </div>
  );
}

Si queremos mostrar valores sólo tenemos que indicar el nombre del objeto y el del campo

import "./styles.css";
import React from "react";

export default function App() {
  const [myClient, setMyClient]  = React.useState({name: 'Pepe', lastname: 'Perez'});

  return (
    <div className="App">
-      <h1>Hello Name Lastname</h1>
+     <h1>Hello {myClient.name} {myClient.lastname}</h1>
      <input />
      <input />
    </div>
  );
}

¿Qué pasa si ahora si queremos actualizar el valor? De primeras podríamos estar tentados a hacer algo así como:

** OJO ESTO ESTA MAL**

import "./styles.css";
import React from "react";

export default function App() {
  const [myClient, setMyClient] = React.useState({name: 'Pepe', lastname: 'Perez'});

  return (
    <div className="App">
      <h1>Hello {myClient.name} {myClient.lastname}</h1>
-      <input />
+      <input value={myClient.name} onChange={(e) => {
+         myClient.name = e.target.value;
+         setMyClient(myClient)
+  }
+ }/>
       <input />
    </div>
  );
}

¿Porque no funciona esto? Al no cambiar el objeto que apunta al estado (cambiamos el contenido, no la dirección de memoria del objeto principal), no se entera de que hay cambios y no repinta.

Lo que tenemos que hacer es asignar una nueva ficha de cliente cuando necesitemos actualizar, para ello hacemos uso del spread operator de ES6:

import "./styles.css";
import React from "react";

export default function App() {
  const [myClient, setMyClient] = React.useState({name: 'Pepe', lastname: 'Perez'});

  return (
    <div className="App">
      <h1>Hello {myClient.name} {myClient.lastname}</h1>
-       <input value={myClient.name} onChange={(e) => myClient.name = e.target.value;setMyClient(myClient)}/>
+       <input value={myClient.name} onChange={(e) => setMyClient({
+         ...myClient,
+         name: e.target.value
+       })}/>
       <input />
    </div>
  );
}

Ahora si podemos ver la aplicación funcionando.

Si quieres dale a la pausa a este video y prueba ahora tu a sacarlo con el apellido:

import "./styles.css";
import React from "react";

export default function App() {
  const [myClient, setMyClient] = React.useState({name: 'Pepe', lastname: 'Perez'});

  return (
    <div className="App">
      <h1>Hello {myClient.name} {myClient.lastname}</h1>
      <input value={myClient.name} onChange={(e) => setMyClient({
         ...myClient,
         name: e.target.value
       })}/>
-       <input />
+      <input value={myClient.lastname} onChange={(e) => setMyClient({
+         ...myClient,
+         lastname: e.target.value
+       })}/>
    </div>
  );
}

Tipado

Si eres de lo que te gusta usar TypeScript, seguro que estarás pensando ¿Y no hay forma de tipar ese useState? La respuesta es SI, partiendo de un ejemplo de codesandbox con typescript primero vamos a crear un interfaz que defina la ficha de un cliente

import "./styles.css";
import React from "react";

+ interface Client {
+   name: string;
+   lastname: string;
+ }

export default function App() {

Y ahora podemos pasarselo como un generico en UseState.

export default function App() {
-  const [myClient, setMyClient] = React.useState({name: 'Pepe', lastname: 'Perez'});
+  const [myClient, setMyClient] = React.useState<Client>({name: 'Pepe', lastname: 'Perez'});

De esta manera cuando pongamos myclient punto lo que sea obtendremos ayuda del IDE y si nos equivocamos escribiendo nos saltará un aviso.

¿Te apuntas a nuestro máster?

Si te ha gustado este ejemplo y tienes ganas de aprender Front End guiado por un grupo de profesionales ¿Por qué no te apuntas a nuestro Máster Front End Online Lemoncode? Tenemos tanto edición de convocatoria con clases en vivo, como edición continua con mentorización, para que puedas ir a tu ritmo y aprender mucho.