5 - Variables de estado de una componente mediante Hook (función useState)

Un Hook de estado es una función especial que nos permite conectarnos a las funciones de la librería de React.

Una componente en React si necesita almacenar valores que luego en forma dinámica se actualizarán en pantalla, lo podemos resolver mediante Hook de estado. Por ejemplo un contador de productos seleccionados, un contador de segundos que se muestra en pantalla, la hora etc.

Debemos importar la función 'useState' si queremos administrar Hook de estados:

import React, { useState } from 'react';

Problema

Crear un nuevo proyecto llamado: proyecto004.
Definir en la interfaz visual un botón que cada vez que se presione se actualice en pantalla un número aleatorio entre 0 y 9.

import { useState } from "react";

function App() {

  function generarAleatorio() {
    const v = Math.trunc(Math.random() * 10);
    setNumero(v)
  }

  const [numero, setNumero] = useState(0);

  return (
    <div>
      <p>Número aleatorio: {numero}</p>
      <button onClick={generarAleatorio}>Generar número aleatorio</button>
    </div>
  );
}

export default App;

Llamamos a la función useState y definimos el valor inicial, en nuestro caso el valor entero cero, la función retorna un arreglo con dos valores que se almacenan en numero y setNumero (podemos definir cualquier nombre para estas dos variables):

  const [numero, setNumero] = useState(0);

La desestructuración de los dos elementos del arreglo que retorna useState es una característica del lenguaje Javascript (ES6)

En la posicion cero del arreglo nos retorna la variable de estado y la posicion uno nos retorna una funcion que nos sirve para actualizar la variable de estado y las almacenamos en 'numero' y 'setNumero'.

Cuando queremos cambiar el valor de la variable de estado llamamos a la función setNumero:

  function generarAleatorio() {
    const v = Math.trunc(Math.random() * 10);
    setNumero(v)
  }

Y cuando queremos rescatar su valor accedemos directamente a la variable de estado 'numero':

      <p>Número aleatorio: {numero}</p>

Luego cuando se presiona el botón se dispara el evento generarAleatorio:

      <button onClick={generarAleatorio}>Generar número aleatorio</button>

Cuando se llama a la función 'setNumero' modificamos la variable de estado, con esto la librería React se encarga de ejecutar nuevamente la graficación de la componente pero solo actualizando los estados cambiados y sin tener que redibujar la página completa (tener en cuenta que se ejectua nuevamente la función App, pero no se crea e inicializa nuevamente la variable de estado con cero. La documentación de React informa que se definió el nombre de la función 'useState' en lugar de 'createState' debido a que solo la primera vez que se llama a 'useState' se crea la variable de estado e inicializa en nuestro ejemplo con cero)

Tenemos como resultado en el navegador:

Hook de estados

Problema

Modificar el problema anterior para que se muestren 5 valores aleatorios. Almacenar los 5 valores en un vector y éste en la variable de estado.

import { useState } from "react";

function App() {

  function generarAleatorios() {
    const vec = new Array(5)
    for (let x = 0; x < vec.length; x++)
      vec[x] = Math.trunc(Math.random() * 10)
    setNumeros(vec)
  }

  const [numeros, setNumeros] = useState([0, 0, 0, 0, 0]);

  return (
    <div>
      <p>Números aleatorios:</p>
      {numeros.map(num => (<p>{num}</p>))}
      <button onClick={generarAleatorios}>Generar números aleatorios</button>
    </div>
  );
}

export default App;

A la función useState podemos pasar tanto tipos de datos primitivos como vectores:

  const [numeros, setNumeros] = useState([0,0,0,0,0]);

En la función generarAleatorios() procedemos a crear un vector de 5 elementos y guardar los 5 valores aleatorios. Seguidamente llamamos a la función 'setNumeros':

  function generarAleatorios() {
    const vec=new Array(5)
    for(let x=0; x<vec.length; x++)
      vec[x]=Math.trunc(Math.random()*10)
    setNumeros(vec)
  }

Para mostrar todos los elemento del vector llamamos al método map de la clase Array y le pasamos una función flecha que retorna para cada elemento del vector un valor encerrado entre las marcas 'p':

      {numeros.map(num => (<p>{num}</p>))}

Problema

Crear un nuevo proyecto con la herramienta create-react-app llamado proyecto005.
Almacenar en el estado de la componente el siguiente vector con artículos:

                  [{
                      codigo: 1, 
                      descripcion: 'papas',
                      precio: 12.52
                   },{
                      codigo: 2, 
                      descripcion: 'naranjas',
                      precio: 21
                   },{
                      codigo: 3, 
                      descripcion: 'peras',
                      precio: 18.20
                   }]

Mostrar en una tabla HTML dichos datos. Cuando se presione un botón borrar el último elemento de la tabla.

import { useState } from "react";

function App() {

  function eliminarUltimaFila() {
    if (articulos.length > 0) {
      const temp=Array.from(articulos)
      temp.pop()
      setArticulos(temp)
    }
  }

  const [articulos, setArticulos] = useState([{
    codigo: 1,
    descripcion: 'papas',
    precio: 12.52
  }, {
    codigo: 2,
    descripcion: 'naranjas',
    precio: 21
  }, {
    codigo: 3,
    descripcion: 'peras',
    precio: 18.20
  }]);

  return (
    <div>
      <table border="1">
        <thead><tr><th>Código</th><th>Descripción</th><th>Precio</th></tr></thead>
        <tbody>
          {articulos.map(art => {
            return (
              <tr key={art.codigo}>
                <td>
                  {art.codigo}
                </td>
                <td>
                  {art.descripcion}
                </td>
                <td>
                  {art.precio}
                </td>
              </tr>
            )
          })}
        </tbody>
      </table>
      <button onClick={eliminarUltimaFila}>Eliminar última fila</button>
    </div>
  );
}

export default App;

Llamamos a la función useState para crear la variable de estado y le pasamos el arreglo de objetos

  const [articulos, setArticulos] = useState([{
    codigo: 1,
    descripcion: 'papas',
    precio: 12.52
  }, {
    codigo: 2,
    descripcion: 'naranjas',
    precio: 21
  }, {
    codigo: 3,
    descripcion: 'peras',
    precio: 18.20
  }]);

Para mostrar los datos del vector nuevamente empleamos la llamada al método 'map' y le pasamos una función anónima. Dentro de la función anónima generamos cada fila de la tabla con los datos de un producto:

      <table border="1">
        <thead><tr><th>Código</th><th>Descripción</th><th>Precio</th></tr></thead>
        <tbody>
          {articulos.map(art => {
            return (
              <tr key={art.codigo}>
                <td>
                  {art.codigo}
                </td>
                <td>
                  {art.descripcion}
                </td>
                <td>
                  {art.precio}
                </td>
              </tr>
            )
          })}
        </tbody>
      </table>

Como podemos comprobar la fila de títulos de la tabla la hacemos previo a llamar a map.

Cuando se presiona el botón llamamos a la función 'eliminarUltimaFila. Primero comprobamos si el vector tiene elementos accediendo al atributo 'length', en caso afirmativo procedemos a crear una copia del vector original llamando al método from y almacenando en una variable temporal el nuevo vector, luego eliminamos el último elemento llamando al método pop y finalmente actualizamos la variable de estado llamando a la función 'setArticulos' (hay que tener en cuenta que debemos crear siempre un nuevo vector con la copia del original y pasar dicho valor):

  function eliminarUltimaFila() {
    if (articulos.length > 0) {
      const temp=Array.from(articulos)
      temp.pop()
      setArticulos(temp)
    }
  }

Problema

Modificar el ejercicio anterior para que aparezca un botón en cada fila de la tabla y permita borrar dicho artículo.

import { useState } from "react";

function App() {

  function borrar(cod) {
    const temp = articulos.filter((art)=>art.codigo !== cod);
    setArticulos(temp)
  }

  const [articulos, setArticulos] = useState([{
    codigo: 1,
    descripcion: 'papas',
    precio: 12.52
  }, {
    codigo: 2,
    descripcion: 'naranjas',
    precio: 21
  }, {
    codigo: 3,
    descripcion: 'peras',
    precio: 18.20
  }]);

  return (
    <div>
      <table border="1">
        <thead><tr><th>Código</th><th>Descripción</th><th>Precio</th><th>Borra?</th></tr></thead>
        <tbody>
          {articulos.map(art => {
            return (
              <tr key={art.codigo}>
                <td>
                  {art.codigo}
                </td>
                <td>
                  {art.descripcion}
                </td>
                <td>
                  {art.precio}
                </td>
                <td>
                  <button onClick={() => borrar(art.codigo)}>Borrar</button>
                </td>
              </tr>
            )
          })}
        </tbody>
      </table>
    </div>
  );
}

export default App;

Cuando generamos ahora la tabla en la última celda debemos disponer un botón que llame a una función y le pase como parámetro el código de artículo a borrar:

                  <button onClick={() => borrar(art.codigo)}>Borrar</button>

Cuando llamamos a una función debemos plantear una función anónima que se le asigna al evento 'onClick'.

La función 'borrar' recibe como parámetro el codigo de artículo a borrar:

  function borrar(cod) {
    const temp = articulos.filter((art)=>art.codigo !== cod);
    setArticulos(temp)
  }

Para borrar un determinado elemento del vector utilizamos el método filter que genera otro vector con todas las componentes que cumplen la condición que le pasamos en la función anónima.

Finalmente actualizamos el estado para que se redibuje la página.

Tenemos como resultado:

Hook de estados

Acotaciones

Las variables de estado pueden almacenar datos de tipo:

  • String.
  • Boolean
  • Number
  • Array
  • Object
  • Undefined

Los valores iniciales del estado se ejecutan solo una vez y es cuando se carga la componente.