Cuando trabajamos con React, podemos utilizar componentes controlados y no controlados. Si bien generalmente se recomienda el uso de los controlados, hay situaciones en las que puede ser ventajoso el uso de los no controlados.
En este blog veremos en detalle en qué consiste cada uno a través de un ejemplo.

Antes de comenzar te compartimos el link del proyecto al cual haremos referencia en este post.

Componentes “controlados”

La característica principal de este tipo de componentes es que su estado es manejado directamente por React. Con estado nos referimos al modelo de datos del componente, en el ejemplo de este post vamos a ver que nuestro estado es una variable llamada “contador” el cual es renderizado en la vista.

¿Cómo maneja React el estado de estos componentes?

En este caso utiliza el hook state. Para importarlo lo hacemos de la siguiente manera:
import { useState } from "react";

Una vez importado hacemos uso de él en el siguiente fragmento de código:

let [contador, setContador] = useState(0);

La función useState recibe un parámetro que inicializa nuestra variable “contador”. Esta función por otro lado nos devuelve dos elementos, por un lado la variable “contador” ya inicializada, y por otra parte una función llamada “setContador” a partir de la cual vamos a poder asignar nuevos valores a nuestra variable.


En el siguiente código, utilizamos setContador a través de funciones handler para actualizar nuestra variable de forma dinámica (en el momento que se actualiza dicho valor, React genera un nuevo render manteniendo sincronizado nuestro modelo de datos y nuestra vista):

<button onClick={aumentarHandler} className="btn-aumentar">Aumentar</button>
<button onClick={reiniciarHandler}><i className="fa fa-refresh"></i></button>
<button onClick={disminuirHandler} className="btn-disminuir">Disminuir</button>

Como podemos ver tenemos tres botones los cuales ejecutan una función distinta. Por medio del atributo onClick definimos una función handler para el evento click de estos. Las funciones handler (aumentarHandler, reiniciarHandler, disminuirHandler) son las que hacen uso de setContador para modificar el estado de nuestra variable.
Veamos el código de estas funciones:

const aumentarHandler = () => {
setContador(++contador);
};
const reiniciarHandler = () => {
setContador(0);
};
const disminuirHandler = () => {
setContador(--contador);
};

Por medio de estos handlers, cada vez que ejecutamos a uno de ellos React genera un nuevo render de nuestra vista, interpolando el valor actualizado de la variable “contador”.

<strong>Valor: </strong> {contador}

Para asimilar mejor esto que acabas de leer te recomendamos ver cómo funciona el componente controlado de este ejemplo en vivo a través del siguiente enlace: Controlled Counter
El objetivo de utilizar este hook es centralizar todo el estado o el modelo de datos del componente en un solo lugar, permitiendo la reactividad (sincronía en cuanto a la actualización del estado con la vista) del componente.

Componentes “no controlados”


Se los conoce de esta forma porque React no controla el valor de un determinado elemento, como por ejemplo un input, un span o un checkbox.
El valor del elemento se almacena internamente. En este caso para manipular los datos del mismo se utiliza directamente el DOM, similar al uso clásico de HTML con vanilla Javascript,.


¿Cómo manejar el estado en este tipo de componentes?

Veamos como hacer el ejemplo del contador a partir de un componente no-controlado.
Para esto ya no vamos a utilizar al hook state, en este caso utilizamos una “ref” (referencia) para obtener los valores directamente desde el DOM.
Para crear una referencia utilizamos el hook ref. Primero importamos el hook de la siguiente manera:

import { useRef } from "react";

Como segundo paso creamos una variable, la cual tendrá asignada la referencia del elemento del DOM que queremos manejar/acceder (en el ejemplo éste elemento sería un span que tendrá como objetivo mostrar el valor de una variable “contador”).

const spanRef = useRef();

useRef siempre regresa un objeto mutable con una única propiedad o atributo llamado current. (para esto último vamos a ver su uso en el paso 4)
Para poder bindear nuestra referencia, por ejemplo, a un span, se usa la propiedad ref en el elemento:

<span ref={spanRef}></span>

Como último paso vamos a actualizar nuestra referencia por medio de las funciones handlers (de la misma forma que en el ejemplo del componente controlado)

const aumentarHandler = () => {
contador = ++contador;
spanRef.current.span = contador;
console.log(spanRef.current.span);
};
const reiniciarHandler = () => {
contador = 0;
spanRef.current.span = contador;
console.log(spanRef.current.span);
};
const disminuirHandler = () => {
contador = --contador;
spanRef.current.span = contador;
console.log(spanRef.current.span);
};

Podemos ver el ejemplo en vivo en el siguiente enlace: Uncontrolled Counter


Vamos a observar que el contador en cuanto a lo que es nuestra vista jamás se actualiza. Incluso si abrimos la sección de “console” vemos que nuestra variable contador incrementa o decrementa su valor en base a las acciones que ejecutamos, pero la vista parece no cambiar ¿tenemos un error? la respuesta es no.


Modificar el valor de la referencia por medio de la propiedad current no produce un nuevo render. Este dato es sumamente importante y se debe tener en cuenta para el momento en que queramos tener nuestro modelo de datos sincronizado con la vista, este hook no es para esa funcionalidad o requerimiento.

¿Entonces para qué podemos utilizar un componente no-controlado?

Son útiles cuando no necesitamos mantener nuestro modelo de datos o negocio sincronizado con la vista.
También sirven para realizar validaciones, con estos componentes tenemos una forma directa de acceder a los datos ingresados por el usuario.
O simplemente para realizar cambios de clases de los elementos referenciados basándose en una determinada condición.
Veamos el último ejemplo Uncontrolled Form, trata de un simple formulario el cual solo requiere un nombre válido (que no sea vacío y que contenga solo caracteres alfabéticos).

 

Conclusión

Con este pequeño ejemplo queremos dejar en claro que no existe una rivalidad entre un componente controlado y un no-controlado. Simplemente son cosas distintas, así como los hook’s state y ref. Por otro lado sugerimos utilizar componentes controlados para cuando nuestra aplicación requiere que nuestra vista se mantenga sincronizada con nuestro modelo de negocio, y utilizar un componente no-controlado cuando no tenemos este requerimiento.

Mandanos tus sugerencias

Ayudanos con ideas para los artículos de este blog a contacto@somospnt.com

¡Seguínos en nuestras redes sociales para enterarte de los últimos posts!