Qwik — Componentes

Claves para entender como trabajar con los componentes en Qwik

Anartz Mugika Ledo🤗
13 min readFeb 9, 2023

Una vez completados los dos primeros artículos del curso empezamos a trabajar con los componentes, que es un elemento super importante para poder refactorizar y reutilizar código en nuestros proyectos.

Debido a la importancia que tiene vamos a trabajar de manera especifica con los componentes, completamente desde 0, como si no supiéramos nada acerca de estos elementos.

Aunque este tema es muy muy básico, conviene hacer un pequeño repaso, para ir paso a paso asentando las bases e ir aumentando la complejidad de lo que es el temario del curso que estoy escribiendo.

Si habéis trabajado en otras tecnologías con componentes, prácticamente os sonarán todos los conceptos y lo podéis usar como repaso adaptando la sintaxis a Qwik, algo que si debemos de tener en cuenta.

Hay que tener en cuenta que estos artículos son detallados pensando en todos los niveles, para animar a más gente a trabajar con este framework, por lo que si tenéis experiencia y algunos aspectos os resultan “absurdos” paciencia por favor.

Todos los artículos publicados del curso los encontraréis en la siguiente lista que iré actualizando semanalmente y estableciendo el orden natural recomendado:

Qwik paso a paso desde 0 al detalle

23 stories

Antes de empezar con los puntos a trabajar, los requisitos para poder poder trabajar en este artículo son los siguientes:

  • Leer todos los artículos en orden hasta llegar a este.
  • Tener instalado la versión de Node LTS 16 ó superior.
  • Sería ideal tener conocimientos muy básicos sobre otras tecnologías como frameworks / librerías como Angular, React, Vue,…
  • Ganas de obtener conocimiento con una nueva tecnología a la vez que lo hago yo.

Esto es lo que vamos a ver en este artículo:

  • Definición de componente.
  • Crear un componente básico.
  • Binding Expressions: Inyectar información en plantilla.
  • Componentes con componentes hijos
  • Inline components.
  • Props: Pasando datos a componentes.
  • Estilos

Quizás os suenen todos los puntos, alguno o ninguno. Independientemente de si os suenan o no, vamos a ver todos con una explicación y sus casos de uso, con varios ejemplos, para poder ir asimilando los conceptos y así poder trabajar en los siguientes puntos.

¿Qué es un componente?

Los componentes son las piezas del puzzle básicas que se forman con una función dentro de una aplicación Qwik que se declaran mediante component$() y, como mínimo, deben devolver un elemento JSX.

Podremos tener componentes dentro de otros componentes y pasar información a través de ellos, para personalizar su contenido y toda su lógica en base a nuestras necesidades.

Sabiendo ya que es un componente, comencemos trabajando con los diferentes apartados, desde la creación del componente hasta aplicar estilos.

Crear componente básico

Para crear un componente básico de Qwik, debemos de importar la siguiente declaración desde el paquete de qwik en el fichero donde vamos a trabajar ( Por ejemplo yo trabajaré en un nuevo proyecto en el fichero inicial: /src/routes/index.tsx)

import { component$ } from '@builder.io/qwik';

Y creamos el componente con esta estructura:

export default component$(() => {
return <>
AÑADIR TODO EL CONTENIDO con notación JSX
</>;
});

Y si ahora queremos crear un componente enviando un saludo como Hola Qwik, espero aprender mucho sobre ti :)

import { component$ } from '@builder.io/qwik';

export default component$(() => {
return <>Hola Qwik espero aprender mucho sobre ti :)</>;
});

Y se verá de la siguiente forma:

Si cambiamos el contenido con una nueva cadena de texto:

import { component$ } from '@builder.io/qwik';

export default component$(() => {
return <>Hola Qwik, soy Anartz y espero aprender mucho sobre ti :)</>;
});

El resultado se refleja de la siguiente forma:

Binding Expressions: Inyectar información en plantilla

El propósito de los componentes es fusionar datos con la plantilla JSX.

Usamos la {expresión} para inyectar datos en un componente dentro del código. Las expresiones se colocan como un nodo de texto o como un atributo en un elemento.

Imaginaros que empezaremos con esta información que queremos inyectar en la plantilla:

const data = {
href: 'https://mugan86.medium.com/', // se añade como atributo
text: 'Anartz Mugika - Tech Blog' // Información que se muestra
};

Teniendo esta información, vamos a inyectarla dentro de un elemento <a></a> para añadir enlaces:

  • Inyectamos data.href en href, que es un atributo de<a><a/>. También añadimos _blank como valor del atributo target para inyectarlo directamente en formato string.
  • Inyectamos data.text en el contenido dentro de<a><a/> y también en el título principal que será un h1.

Lo añadiremos de la siguiente forma, teniendo en cuenta que la constante data tiene como valores lo siguiente:

import { component$ } from '@builder.io/qwik';

export default component$(() => {
const data = {
href: 'https://mugan86.medium.com/',
text: 'Anartz Mugika - Tech Blog'
};
return (
<>
<h1>{ data.text }</h1>
<br />
<a href={ data.href } target={ '_blank' }>{ data.text }</a>
</>
);
});

El resultado que se obtiene es lo siguiente:

Inyectar información de manera condicional

También podemos renderizar e inyectar información de manera condicional y podemos iterar:

Añadiendo de la siguiente forma:

{  ( CONDICION_A_EVALUAR ) ? 'SE CUMPLE': ' NO SE CUMPLE'   }

Adaptándolo al código, tendremos dos valores iniciales y vamos a comprobar la condición de age, para ver si cumple o no.

  • Si cumple deberá de mostrar un mensaje así: Como "tienes más de 40 años", tienes 18 años y más de 22 años de experiencia ;)
  • Si no se cumple la condición: Eres joven, todavía tienes 37 años y te faltan 3 para volver a cumplir 18 años ;)

Fijaros que en el punto no se cumple he puesto 3 años... también podremos realizar operaciones matemáticas, concatenaciones de dos valores ó más...

El componente se construirá de la siguiente forma:

export default component$(() => {
</strong> const age = 37;
const name = 'Anartz';
return (
<>
<h1>{ name }&#x3C;/h1>
<br />
<p>
{
( age > 40 ) ?
'Como "tienes más de 40 años", tienes 18 años y más de 22 años de experiencia ;)' :
`Eres joven, todavía tienes ${ age } años y te faltan ${ 40 - age } para volver a cumplir 18 años ;)`
}
</p>
</>
);
});

Y su resultado será el siguiente, que como el valor de la edad NO CUMPLE muestra este mensaje:

Si cambiamos el valor de age por uno superior a 40 muestra el siguiente mensaje, ya que SI CUMPLE la condición:

Inyectar información mediante el uso de estructuras repetitivas

También podemos renderizar estructuras de tipo bucle dentro como por ejemplo esta lista de hobbies:

<ul>
{
['leer', 'deporte', 'videojuegos' ].map(
(value, index) => (<li>{index + 1} - {value}</li>)
)
}
</ul>

Añadiendo este código al anterior desarrollado:

export default component$(() => {
const age = 37;
const name = 'Anartz';
return (
<>
<h1>{name}</h1>
<br />
<p>
{age > 40
? 'Como "tienes más de 40 años", tienes 18 años y más de 22 años de experiencia ;)'
: `Eres joven, todavía tienes ${age} años y te faltan ${
40 - age
} para volver a cumplir 18 años ;)`}
</p>
<ul>
{['leer', 'deporte', 'videojuegos'].map((value, index) => (
<li>
{index + 1} - {value}
</li>
))}
</ul>
</>
);
});

El resultado será el siguiente:

  1. Título en el que inyectamos el valor name.
  2. Texto condicional que NO cumple la condición ya que age es menor que 40 siendo su valor 37.
  3. Iteración en lista de los 3 hobbies añadidos.

Componentes con componentes hijos / Inline components

Los componentes se componen de otros componentes para crear las aplicaciones y lo recomendable es que se haga uso de ellos, sobre todo pensando en reutilizarlos para usos similares, como podrían ser los botones de acción, listas, elementos tipo select,…

Uno de los superpoderes de Qwik radica en sus características de carga diferida cuyas características podemos resumirlas de la siguiente manera:

  • Independiente de la jerarquía: los componentes se pueden cargar desordenados. Por ejemplo, el código de un componente secundario puede cargarse antes que su código principal.
  • Según la interacción: la carga del código se difiere hasta que un usuario interactúa con un componente.
  • Más que solo componentes: Qwik carga de forma diferida cualquier cierre, incluidos componentes, detectores de eventos, efectos y comportamientos.

El símbolo$ marca un cierre como de carga diferida. Por ejemplo, el método component$() hace que el componente se comporte cargándose de forma diferida. Cuando veamos un $ en el código Qwik, estaremos cruzando un límite de carga diferida y deberemos de tener en cuenta las reglas especiales:

  • Cualquier variable de alcance léxico debe declararse como const .
  • una variable/símbolo obtenido debe ser: a.- serializable / b.- importable (ya sea desde un archivo diferente como import o desde este archivo usando export).

Si queremos asegurarnos de que un componente se carga con otro componente de manera diferida, debemos de crear un componente en línea.

Los componentes en línea se cargan como parte del componente principal y son equivalentes a como la mayoría de los otros frameworks tratan con los componentes sin usar el método component$().

Siguiendo el ejemplo que tenemos, podemos separar ese código en tres componentes en línea:

  • Título: <Title />
  • Apartado del mensaje del condicional: <Age />
  • Lista de hobbies: <Hobbies />

Haciendo la separación con el objetivo de optimizarlo, lo dejamos de la siguiente manera:

export const Title = () => {
const name = 'Anartz';
return <h1>{name}</h1>;
};

export const Age = () => {
const age = 37;
return (
<p>
{age > 40
? 'Como "tienes más de 40 años", tienes 18 años y más de 22 años de experiencia ;)'
: `Eres joven, todavía tienes ${age} años y te faltan ${
40 - age
} para volver a cumplir 18 años ;)`}
</p>
);
};
export const Hobbies = () => {
return (
<ul>
{['leer', 'deporte', 'videojuegos'].map((value, index) => (
<li>
{index + 1} - {value}
</li>
))}
</ul>
);
};
// Componente principal
export default component$(() => {
return (
<>
<Title />
<br />
<Age />
<Hobbies />
</>
);
});

Si vamos al navegador, seguiremos viendo lo mismo sabiendo que ya lo tenemos en 3 partes principales:

No se si os habéis fijado, pero los componentes que se han creado, lo hemos hecho con export por delante aun estando en el mismo fichero. Podéis pensar que no es necesario, pero si lo es ya que tenemos que exportarlos debido a que se van a cargar de manera perezosa en base a nuestras necesidades y esto justo es lo que se ha comentado en esta característica:

importable (ya sea desde un archivo diferente como import o desde este archivo usando export).

Entonces, ¿Qué pasaría si por ejemplo le quito export al primer componente en línea llamado Title?

Vamos a verlo, lo dejamos así guardando los cambios:

...
const Title = () => {
const name = 'Anartz';
return <h1>{name}</h1>;
};
...

Una vez que se guardan los cambios, nos escupe un error en consola como el siguiente donde nos da la descripción del error que es bastante claro:

Y en el navegador el mismo error:

El error es bien claro:

[plugin:vite-plugin-qwik] Reference to identifier declared at the root 'Title'. It needs to be exported in order to be used inside a Qrl($) scope.

¿Esto que significa? Es sobre la referencia al identificador declarado en la raíz Title que debe de exportarse para poder usarse dentro de un ámbito Qrl($) que en este caso sería el componente principal que tiene la función component$().

Por lo tanto, volvemos a exportarlo, como estaba antes para seguir adelante:

export const Title = () => {
const name = 'Anartz';
return <h1>{name}</h1>;
};
...

Ahora ya tendremos todo ok. He hecho hincapié en esto debido a que cuando hemos trabajado en otras tecnologías como React, si creamos el componente dentro del mismo fichero no era necesario usar el export como en este caso.

Ejemplo en React:

Aclarado esto, ya podemos dar por finalizado el apartado de añadir componentes dentro de otros componentes y ahora vamos al siguiente punto que será el último antes de hacer una introducción a añadir estilo, para poder trabajar con los Props.

Props: Pasando datos a componentes.

Las aplicaciones web se construyen a partir de componentes de la misma manera que las aplicaciones generales se construyen a partir de funciones.

La composición de funciones no sería muy útil si no se pudieran pasar parámetros. De la misma manera que las funciones tienen parámetros, los componentes tienen props.

¿Qué es un prop?

Cuando hablamos de prop hará referencia a las propiedades las cuales cumplen un rol importante en el proceso de desarrollo de una aplicación o página web constituido por un componente o un número concreto de componentes

Un componente usa props para pasar datos a sus componentes secundarios en los cuales vamos a recibir esa información y la vamos a inyectar en el componente hijo e incluso, si hubiesen hijos en este componente, pasar la información a estos también hasta llegar a un punto final.

Voy a modificar el componente Title y voy a añadirle la opción para recoger información mediante props.

Ahora mismo usamos la constante name, pero vamos a añadirle también tipo de saludo, así veremos que podemos pasarle uno ó más props sin problemas.

Especificamos con una interface los props de Title:

interface TitleProps {
greetingText: string;
name: string;
}

Y modificamos el componente Title, añadiendo en el apartado de parámetros, para que obtenga un objeto con los props especificados en la interface e inyectamos los valores, eliminando la constante con el valor Anartz. Así vamos a conseguir más dinamismo:

interface TitleProps {
greetingText: string;
name: string;
}
export const Title = (props: TitleProps) => {
return <h1>{props.greetingText} {props.name}</h1>;
};

Una vez almacenados los cambios, nos aparece un error y esto es debido a que hemos especificado en la interface que las dos propiedades de TitleProps son obligatorias y en este momento no estamos pasando nada:

Por lo tanto, tenemos que pasar los valores desde el componente padre, de esta manera, inyectando el valor en la propiedad especificada, en este caso sería con:

  • greetingText
  • name

Quedando de la siguiente forma:

export default component$(() => {
return (
<>
<Title greetingText={'Hola'} name={'Anartz'}/>
<br />
<Age />
<Hobbies />
</>
);
});

Y el resultado debería de ser como el siguiente:

Podemos reutilizar ese componente añadiendo otro título:

export default component$(() => {
return (
<>
<Title greetingText={'Hola'} name={'Anartz'}/>
...
<Title greetingText={'Adios'} name={'Qwik'}/>
</>
);
});

Quedando de la siguiente forma:

Bien, sabiendo esto os propongo una pequeña tarea:

Modificad el código para que en el componente principal podamos pasarle la edad (age) al componente Age y enviaremos los hobbies a Hobbies, todo esto mediante props que en el primer caso podríamos llamar AgeProps y en el segundo HobbiesProps .

Os pido que lo intentéis, no es difícil y así podemos practicar lo visto. El resultado os lo dejo aquí

El resultado visual es el mismo, pero evidentemente mucho mejor y más personalizable, con lo que estaremos aplicando mejores prácticas.

Reutilización de los componentes

Si ahora quisiéramos utilizar estos componentes en el futuro en otras páginas, lo ideal sería que traslademos a ficheros independientes los componentes dentro de esta estructura añadiendo el código de cada uno de ellos tal cual como lo teníamos dentro del fichero index.tsx del directorio src/routes

src/
└── components/
└── title/
└── index.tsx
└── age/
└── index.tsx
└── hobbies/
└── index.tsx

Y después de la refactorización, el fichero index.tsx de src/routes quedará así:

import { component$ } from '@builder.io/qwik';
import { Age } from '~/components/age';
import { Hobbies } from '~/components/hobbies';
import { Title } from '~/components/title';

export default component$(() => {
return (
<div>
<Title greetingText={'Hola'} name={'Anartz'} />
<br />
<Age age={37} />
<Hobbies hobbies={['leer', 'deporte', 'videojuegos']} />
<Title greetingText={'Adios'} name={'Qwik'} />
</div>
);
});

Estilos

Los estilos son una parte importante del diseño de una aplicación web, aunque muchas veces se infravaloren son muy importantes para dar un toque personalizado a nuestros proyectos que pueden marcan la diferencia.

Qwik es responsable de cargar la información de estilo cuando se monta un componente.

Los añadimos ahora mismo en línea:

...
export const Title = (props: TitleProps) => {
return (
<h1 style="color:blue;font-size:46px;">
{props.greetingText} {props.name}
</h1>
);
};

Y aunque se visualizan los cambios correctamente:

De esta manera funciona, pero no es la correcta en Qwik, en uno de los próximos capítulos, cuando hablemos de los estilos, trabajaremos con funciones como useStyles$() y useStylesScoped$() para aplicar buenas prácticas.

Ahora en este momento lo vamos a dejar así, como base inicial enfocado a los componentes. Para separarlos con colores y así verlos visualmente como tenemos estructurado este componente principal con los tres componentes hijos o complementarios.

Vamos a añadirle un borde al componente principal y a los hijos, de esta manera:

Title

export const Title = (props: TitleProps) => {
return (
<div style="border:2px solid red; padding: 5px">
<h1 style="color:blue;font-size:46px;">
{props.greetingText} {props.name}
</h1>
</div>
);
};

Age

export const Age = (props: AgeProps) => {
const age = props.age;
return (
<div style='border:2.5px solid yellow; padding: 5px'>
<p>
...
</p>
</div>
);
};

Hobbies

export const Hobbies = (props: HobbiesProps) => {
const hobbies = props.hobbies;
return (
<div style='border:4px solid green; padding: 5px'>
<ul>
....
</ul>
</div>
);
};

Componente principal

export default component$(() => {
return (
<div style="border: 3px dotted purple; padding: 10px">
...
</div>
);
});

Cuyo resultado será el siguiente:

Conclusión

Hemos trabajado con un concepto muy básico como el uso de componentes. Hemos empezado desde lo más básico hasta dejar todo ya bien definido con propiedades y estilos, con diferentes casos en los que funcionará todo bien y aplicando las mejores prácticas posibles.

También hemos hablado de las situaciones en los que sufríamos errores / problemas que servirán como referencia para detectar los diferentes errores y sus posibles vías de solución.

Teniendo los conocimientos básicos, enrutamiento (Routing) y componentes podemos pasar al siguiente artículo donde trabajaremos con las plantillas, haciendo uso principalmente de componentes y diferentes modos de enrutamiento. ¡¡Nos vendrá perfecto para repasar!!

Todos los artículos publicados del curso los encontraréis en la siguiente lista que iré actualizando semanalmente y estableciendo el orden natural recomendado:

Qwik paso a paso desde 0 al detalle

23 stories

Presencia en redes sociales

Podéis encontrarme en las siguientes redes.

--

--

Anartz Mugika Ledo🤗

[{#frontend:[#mobile:{#android, #kotlin, #ionic}}, {#web:{#angular, #qwik, #bootstrap}}],{#backend: [{#graphql, #nestjs,#express, #mongodb, #mysql}]}]