Construyendo componentes

Estaba leyendo el último artículo de Dan Abramov The two Reacts, donde habla sobre los dos tipos de React. Les invito a leerlo porque es muy interesante, y algo me llamó mucho la atención.
Dan utiliza un recurso matemático para explicar como ve a las interfaces de usuarios:
UI = F(estado)
Y no se refiere a que el código de la interfaz de usuario debe ser una sola función que toma el estado como argumento. Si no más bien se lee como que la UI es una función de estado. En otras palabras, el estado determina la interfaz de usuario y la interfaz de usuario debe reinterpretarse cuando cambia el estado.
Este enfoque es muy importante en la forma en la que se construyen los componentes, porque hasta en la decisión de no usar un estado se cumple. Cuando estamos en ese proceso de diseño que realizamos en nuestras cabezas es clave tener presente esa idea. Pero, ¿Cómo podemos usar esa idea a nuestro favor?
Les comparto parte de mi proceso para entender que tipo de componente quiero construir, girando alrededor de la pregunta: ¿Qué parte de la UI quiero que se actualice?
Pensando en estados
Los estados son el conjunto mínimo de información que mi aplicación tiene que recordar y que en cuanto varíe va a disparar el proceso de reinterpretar lo que estamos dibujando. Para saber que estados voy a necesitar tener, poder identificar la responsabilidad del componente es un factor muy importante.
Componentes de negocio
Una de las primeras cosas que me gusta pensar en lo que tiene que resolver nuestro componente y si eso está asociado directamente con la actividad de lo que esté desarrollando o si es algo más común a los proyectos web como tales.
Tomemos de ejemplo un portal de administración de asociados para clubes y tenemos que hacer un componente que visualiza todos los socios de un club con un listado. Lo primero es identificar las responsabilidades:
Conoce como obtener la información de los asociados - lógica de negocio
Renderizar una tabla - lógica de presentación
Con esta separación ya podemos decidir si vamos a mantener las responsabilidades en el mismo componente o no. En nuestro ejemplo sabemos que vamos a tener otras tablas en nuestra aplicación, por lo que tendría sentido querer separarlo.
function TablaSocios(){ // Se encargar de conocer de donde obtener la informacion // Tambien podriamos procesar la informacion si necesitaramos. return <Tabla /> } function Tabla({data, columns}){ return ( <table> <tr> {columns.map((column)=><td>{column}</td> </tr> {data.map(item => <tr> <td>{item.name}</td> <td>{item.email}</td> </tr>)} </table> ) }
No solo preguntarnos si es un componente de negocio nos puede guiar si no también el contexto donde estamos armando nuestro componente: ¿ese estado necesita estar en mi componente? ¿Podemos dejar que el padre se encargue del estado? ¿El estado debe ser compartido por los componentes? ¿Podemos atar el componente a quien lo implementa? ¿O debe ser independiente?
Todas estas preguntas nos van a definir donde queremos poner los estados y si es algo que solo se necesita para cambiar la UI o vamos a manejar data de la aplicación.
Tip: Reflexionar sobre qué parte de la aplicación debe controlar cada componente te ayudará a evitar la duplicación de la lógica en diferentes lugares. Para profundizar más en cómo diseñar la lógica de negocio de manera limpia y eficaz, podrías revisar el artículo sobre Introducción a la Programación Funcional, que promueve la separación y claridad en el código.
Cuidado con el doble estado. Tener un estado en el padre que sea el valor predeterminado de nuestro estado interno puede llevarnos a tener que suscribirnos a los cambios.
function TablaSocios(){ const [state, setState] = useState([]) return <Table data={state} /> } function Table({data}){ const [state, setState] = useState(data) useEffect(() => { setState(data) }, [data]) ... }
Visuales vs. Lógicos
No pensemos que un componente por ser de presentación no va a tener lógica. Supongamos que queremos poder cambiar el orden de los socios haciendo clics en las columnas, eso tiene una parte de lógica donde hay que reordenar la información que nos llega (suponiendo que vamos a hacer ese ordenado en el front y no en el back).
Queriendo manejar todas las responsabilidades de la tabla podríamos terminar escribiendo un componente Tabla gigante donde tengamos la parte que se encargue de procesar la información que llega, el que escucha el evento del clic sobre una columna y quien termina dibujando al final la UI.
¡O podríamos dividirlo!
function Tabla({data, columns}){ const [sort, setSort] = useState({field: "name", order: "desc"}) const sortBy = () => { ... } const handleChangeSort = (column) => setSort(prevState => { if(prevState === column){ return {...prevState, order: "asc"} } return {...prevState, name: column} }) const sortedData = data.sort(sortBy) return ( <TablaUI data={sortedData} columns={columns} onChangeSort={handleChangeSort} /> ) } function TablaUI({data, columns, onChangeSort}){ return ( <table> <tr> {columns.map((column)=> ( <td onClick={() => onChangeSort(column)}> {column} </td> )} </tr> {data.map(item => <tr> <td>{item.name}</td> <td>{item.email}</td> </tr>)} </table> ) }
Nota: Para conocer otras prácticas que te ayuden a mantener tu código ordenado y a gestionar la lógica de manera eficiente, puedes explorar Automatización de pruebas de software, que te ayudará a detectar posibles errores de forma temprana y mantener tu aplicación estable.
En conclusion
Identificar que tipo de componente estamos haciendo nos ayuda a pensar como lo queremos escribir, pero siempre con la idea en la cabeza de que la UI va a estar regida por el estado.
Y que a veces pensar en armar una sola pantalla pueden terminar siendo un montón de componentes trabajando juntos cada uno con sus responsabilidades y no conociendo nada más allá de lo que necesita, actualizándose solos aquellos que necesitan reinterpretarse según la data del estado cambie.
Para terminar, te recomendamos revisar también la sección de Cinco cualidades clave de una API si tu proyecto involucra servicios o endpoints, ya que la forma en que estructuras tus datos y mantienes la coherencia entre el cliente y el servidor puede impactar significativamente la forma en que manejas tus componentes y estados.
🐶