From 9d3031e27a517da221da099ba09614c8ecd36824 Mon Sep 17 00:00:00 2001 From: ezemastro Date: Sat, 14 Feb 2026 20:34:09 +0000 Subject: [PATCH 01/27] Add spanish translation of: blog/preact-x.md --- content/es/blog/preact-x.md | 121 ++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 content/es/blog/preact-x.md diff --git a/content/es/blog/preact-x.md b/content/es/blog/preact-x.md new file mode 100644 index 000000000..c938359e3 --- /dev/null +++ b/content/es/blog/preact-x.md @@ -0,0 +1,121 @@ +--- +title: Preact X, una historia de estabilidad +date: 2024-05-24 +authors: + - Jovi De Croock +translation_by: + - Ezequiel Mastropietro +--- + +# Preact X, una historia de estabilidad + +Muchos de ustedes han estado esperando la llegada de [Preact 11](https://github.com/preactjs/preact/issues/2621), anunciada en una Issue abierta en julio de 2020, y, a decir verdad, yo era una de las personas más emocionadas por la v11. + +Cuando empezamos a pensar en Preact 11, creíamos que no había forma de introducir los cambios que teníamos en mente en Preact X sin cambios que rompieran la compatibilidad; algunas de las cosas que teníamos en mente: + +- Usar una estructura de VNode de respaldo para reducir el GC (recolección de basura); al hacer esto, solo usaríamos el resultado de `h()` para actualizar nuestro nodo de respaldo. +- Rendimiento del reconciliador, para permitir rutas optimizadas para montaje/desmontaje/etc. +- Algunos cambios como eliminar el sufijo `px`, `forwardRef` y romper el soporte para IE11. +- Mantener `ref` en las props +- Abordar errores en el diffing de eventos e hijos. + +Estos eran nuestros objetivos iniciales para la v11, pero al recorrer este camino, nos dimos cuenta de que muchos de esos cambios no eran realmente cambios que rompían la compatibilidad y podían lanzarse directamente en la v10 de una manera no disruptiva. Solo el tercer punto, eliminar el sufijo `px` y pasar `ref` directamente en las props, así como eliminar el soporte de IE11, entran en la categoría de cambios que rompen la compatibilidad. Decidimos lanzar las otras características en la línea de versiones estables de la v10, lo que permite a cualquier usuario de Preact beneficiarse de ellas inmediatamente sin tener que cambiar su código. + +Preact tiene una base de usuarios mucho mayor hoy en día en comparación con cuando hicimos los planes originales para la v11. Goza de un amplio uso en muchas empresas, de pequeñas a grandes, para software de misión crítica. Realmente queremos asegurarnos de que cualquier cambio que rompa la compatibilidad que podamos introducir valga absolutamente el costo de mover todo el ecosistema hacia él. + +Mientras estábamos [experimenting](https://github.com/preactjs/preact/tree/v11) optamos por un nuevo tipo de diffing, llamado +[skew based diffing](https://github.com/preactjs/preact/pull/3388) (diffing basado en sesgo), vimos mejoras reales de rendimiento, así como la corrección de un montón de errores de larga data. A medida que pasaba el tiempo e invertíamos más tiempo en estos experimentos para Preact 11, empezamos a notar que las mejoras que estábamos logrando no necesitaban ser exclusivas de Preact 11. + +## Lanzamientos + +Desde la mencionada incidencia de Preact 11 ha habido 18 (!!) versiones menores de Preact X. +Muchas de ellas han sido inspiradas directamente por el trabajo realizado en Preact 11. Repasemos algunas y veamos el impacto. + +### 10.5.0 + +La introducción de la [hidratación reanudada](https://github.com/preactjs/preact/pull/2754) -- esta funcionalidad básicamente permite suspender durante la re-hidratación de tu árbol de componentes. Esto significa que, por ejemplo, en el siguiente árbol de componentes re-hidrataremos y haremos que el `Header` sea interactivo mientras el `LazyArticleHeader` se suspende; mientras tanto, el DOM renderizado en el servidor permanecerá en pantalla. Cuando la carga diferida termine continuaremos re-hidratando, se podrá interactuar con nuestro `Header` y `LazyArticleHeader` mientras nuestros `LazyContents` se resuelven. Esta es una característica bastante potente para hacer que tus cosas más importantes sean interactivas sin sobrecargar el tamaño del bundle/tamaño de descarga de tu bundle inicial. + +```jsx +const App = () => { + return ( + <> +
+
+ + + +
+ +
+
+
+
+ + ) +} +``` + +### 10.8.0 + +En la 10.8.0 introdujimos el [asentamiento de estado](https://github.com/preactjs/preact/pull/3553), esto aseguraría que si un componente actualiza el estado de un hook durante el renderizado, nosotros lo detectaríamos, cancelaríamos los efectos anteriores y continuaríamos renderizando. Por supuesto, tendríamos que asegurarnos de que esto no entrara en bucle, pero esta característica reduce la cantidad de renderizados que se encolan debido a invocaciones de estado dentro del renderizado; esta característica también aumentó nuestra compatibilidad con el ecosistema de React, ya que muchas bibliotecas confiaban en que los efectos no se llamaran varias veces debido a actualizaciones de estado dentro del renderizado. + +### 10.11.0 + +Después de mucha investigación encontramos una manera de introducir [useId](https://github.com/preactjs/preact/pull/3583) n Preact, esto requirió un montón de investigación sobre cómo podríamos agregar valores únicos para una estructura de árbol dada. Uno de nuestros mantenedores escribió sobre +[nuestra investigación en ese momento](https://www.jovidecroock.com/blog/preact-use-id) y hemos iterado sobre ello desde entonces tratando de hacerlo tan libre de colisiones como sea posible... + +### 10.15.0 + +Encontramos que un re-renderizado de paso que resultaba en múltiples componentes nuevos re-renderizándose podía resultar en que nuestra `rerenderQueue` estuviera desordenada; esto podía resultar en que nuestras actualizaciones (de contexto) se propagaran a componentes que luego se renderizarían *de nuevo* con valores obsoletos, ¡puedes consultar [el mensaje del commit](https://github.com/preactjs/preact/commit/672782adbf9ccefa7a4d7c175f0adf8580f73c92) para una explicación realmente detallada! Hacer esto tanto agrupa estas actualizaciones como aumenta nuestra alineación con las bibliotecas de React. + +### 10.16.0 + +En nuestra investigación para la v11 profundizamos en el diffing de hijos ya que éramos conscientes de que había algunos casos donde nuestro algoritmo actual se quedaba corto, simplemente listando algunas de estas incidencias: + +- [eliminar un elemento antes que otro causaría una re-inserción](https://github.com/preactjs/preact/issues/3973) +- [re-inserciones al eliminar más de 1 hijo](https://github.com/preactjs/preact/issues/2622) +- [desmontaje innecesario de nodos con keys](https://github.com/preactjs/preact/issues/2783) + +No todas estas resultaban en un mal estado, algunas solo significaban una disminución del rendimiento... Cuando descubrimos que podíamos portar el diffing basado en sesgo a Preact X estábamos emocionados, ¡no solo arreglaríamos muchos casos, sino que podríamos ver cómo se comporta este algoritmo en el mundo real! Lo cual, en retrospectiva, funcionó muy bien; a veces hubiese deseado que tuviéramos buenos bancos de pruebas para ejecutar esto primero en lugar de que nuestra comunidad tuviera que reportar incidencias. Quiero aprovechar esta oportunidad para agradecerles a todos por ayudarnos enviando siempre incidencias consideradas con reproducciones, ¡ustedes son absolutamente los mejores! + +### 10.19.0 + +En la 10.19.0 Marvin aplicó su investigación de [fresh](https://fresh.deno.dev/) para agregar [funciones JSX pre-compiladas](https://github.com/preactjs/preact/pull/4177), esto básicamente te permite pre-compilar tus componentes durante la transpilación, cuando se ejecuta render-to-string solo tenemos que concatenar las cadenas en lugar de asignar memoria para todo el árbol de VNodes. ¡La transformación para esto es exclusiva de Deno por el momento, pero el concepto general está presente en Preact! + +### 10.20.2 + +Hemos enfrentado una serie de incidencias donde un evento podía burbujear hacia un VNode recién insertado, lo que resultaba en un comportamiento no deseado; esto se solucionó [agregando un reloj de eventos](https://github.com/preactjs/preact/pull/4322). En el siguiente escenario, harías clic en el botón que establece el estado, el navegador intercala el burbujeo de eventos con micro-ticks, que también es lo que usa Preact para programar actualizaciones. Esta combinación significa que Preact actualizará la UI, lo que significa que el `
` obtendrá ese manejador `onClick` hacia el cual burbujearemos e invocaremos el `click` de nuevo, apagando inmediatamente este estado otra vez. + +```jsx +const App = () => { + const [toggled, setToggled] = useState(false); + + return toggled ? ( +
setToggled(false)}> + clear +
+ ) : ( +
+ +
+ ); +}; +``` + +## Estabilidad + +Los anteriores son algunos lanzamientos seleccionados de cosas que nuestra comunidad recibió _sin_ cambios que rompen la compatibilidad, pero hay mucho más... Agregar una nueva versión mayor siempre deja atrás a una parte de la comunidad y no queremos hacer eso. Si miramos la línea de lanzamientos de Preact 8 podemos ver que todavía hay 100.000 descargas en la última semana, el último lanzamiento de 8.x fue hace 5 años, solo para mostrar que una parte de la comunidad se queda atrás. + +La estabilidad es genial, nosotros como el equipo de Preact amamos la estabilidad. De hecho lanzamos múltiples características mayores en otros proyectos del ecosistema: + +- [Signals](https://github.com/preactjs/signals) +- [Renderizado asíncrono](https://github.com/preactjs/preact-render-to-string/pull/333) +- [Renderizado en streaming](https://github.com/preactjs/preact-render-to-string/pull/354) +- [Prefresh](https://github.com/preactjs/prefresh) +- [El preset de vite con pre-renderizado](https://github.com/preactjs/preset-vite#prerendering-configuration) +- [Un nuevo enrutador asíncrono](https://github.com/preactjs/preact-iso) +- [Create Preact](https://github.com/preactjs/create-preact) + +Valoramos nuestro ecosistema y valoramos las extensiones que se construyen a través de nuestra [`options API`](https://www.google.com/search?q=%5Bhttps://marvinh.dev/blog/preact-options/%5D(https://marvinh.dev/blog/preact-options/)), este es uno de los principales impulsores detrás de no querer introducir estos cambios que rompen la compatibilidad, sino en su lugar, permitirles a todos beneficiarse de nuestra investigación sin un camino de migración doloroso. + +Esto no significa que Preact 11 no sucederá, pero podría no ser lo que pensamos inicialmente que sería. En su lugar, podríamos simplemente eliminar el soporte de IE11 y darles esas mejoras de rendimiento, todo mientras les damos la estabilidad de Preact X. Hay muchas más ideas flotando y estamos muy interesados en la experiencia más amplia de Preact en el contexto de meta-frameworks que proporcionan cosas como enrutamiento listo para usar. Estamos explorando este ángulo en nuestro preset de vite así como en [Fresh](https://fresh.deno.dev/) para tener una buena idea de cómo debería verse un meta framework que priorice Preact. \ No newline at end of file From 5db82f907800f3ed5291cdba9076c1be3e4ff2ef Mon Sep 17 00:00:00 2001 From: ezemastro Date: Sat, 14 Feb 2026 20:34:48 +0000 Subject: [PATCH 02/27] Add spanish translation for: blog/prerendering-preset-vite.md --- content/es/blog/prerendering-preset-vite.md | 368 ++++++++++++++++++++ 1 file changed, 368 insertions(+) create mode 100644 content/es/blog/prerendering-preset-vite.md diff --git a/content/es/blog/prerendering-preset-vite.md b/content/es/blog/prerendering-preset-vite.md new file mode 100644 index 000000000..0031f37bc --- /dev/null +++ b/content/es/blog/prerendering-preset-vite.md @@ -0,0 +1,368 @@ +--- +title: Prerendering with `@preact/preset-vite` +date: 2024-08-06 +authors: + - Ryan Christian +translation_by: + - Ezequiel Mastropietro +--- + +# Prerenderizado con Preset Vite + +Ha pasado medio año desde que nuestro plugin de prerenderizado se volvió disponible de manera algo silenciosa en `@preact/preset-vite`, así que hablemos de ello, un poco de nuestra historia y del ecosistema en general. + +Aquellos que han estado en nuestra comunidad por un tiempo saben cuánto nos gusta el prerenderizado; fue una característica de primera clase en Preact-CLI, luego en WMR, y ahora en nuestro preset de Vite. Cuando se hace bien, es una adición sin dolor a la típica SPA que mejora enormemente la experiencia del usuario, y nuestro plugin de prerenderizado tiene como objetivo facilitar exactamente eso. + +## ¿Qué es el Prerenderizado? + +"Prerenderizado" (Prerendering), para el contexto de este post, es el acto de generar HTML desde tu aplicación en tiempo de compilación (build time) usando renderizado del lado del servidor (SSR); a veces esto también puede referirse como generación de sitios estáticos (SSG). + +Aunque no profundizaremos en las virtudes del SSR aquí, ni argumentaremos que debas usarlo, generalmente es ventajoso enviar un documento HTML completamente poblado al usuario en la navegación inicial (y tal vez en navegaciones posteriores también, dependiendo de la estrategia de enrutamiento) en lugar de un "shell" vacío en el que el JS del lado del cliente eventualmente renderizará. Los usuarios obtendrán acceso al documento más rápido y pueden comenzar a usar la página (aunque, a menudo con funcionalidad reducida) mientras el JS todavía se está descargando en segundo plano. + +## Nuestra Historia en el Espacio + +Desde que Preact-CLI llegó a su lanzamiento público allá por mayo de 2017, el prerenderizado integrado ha sido un pilar en nuestras herramientas de compilación; felizmente lo llevamos adelante en WMR en 2020 y fue algo que nosotros y los miembros de la comunidad extrañamos mucho cuando cambiamos a sugerir Vite. + +Si bien cada iteración ha sido un poco diferente, todas se han construido alrededor de la misma idea central: los usuarios adoptarán más fácilmente el prerenderizado cuanto más simple sea de configurar, incluyendo cambios limitados en su base de código existente. En Preact-CLI, esto significaba proporcionar una exportación predeterminada del componente raíz con algunos datos JSON para poblarlo; en WMR y ahora en Vite, significa exportar una función simple `prerender()` que devuelve el HTML para la ruta, con el prerenderizador recorriendo la aplicación por sí mismo, reemplazando la necesidad de JSON por adelantado. + +Cualquiera que haya trabajado extensamente con SSR a escala sabe que hay una montaña de complejidad que nunca se puede abstraer completamente y no argumentaríamos lo contrario. Sin embargo, casi cada SPA proporciona una mejor experiencia si está prerenderizada y por eso queremos subir a bordo a tantos usuarios como sea posible -- reducir la barrera de entrada ha demostrado ser tremendamente exitoso en nuestra comunidad, por lo que ha sido una parte clave de nuestra filosofía de diseño apuntar a que sea lo más "drop-in" (de integración inmediata) posible. + +## Ecosistema Vite Existente + +Antes de crear nuestra propia implementación de prerenderizado para nuestro preset de Vite, echamos un vistazo al ecosistema existente de Vite para ver qué se ofrecía, pero no encontramos exactamente lo que buscábamos con las opciones. El prerenderizado es mejor cuando es lo más cercano posible a "drop-in", tomando tu aplicación existente, con mínima modificación, y generando HTML a partir de ella, pero las soluciones existentes estaban un paso más lejos de "drop-in" de lo que nos hubiera gustado y caían en dos categorías principales: + +1. Múltiples Compilaciones (Builds) + - Compilaciones separadas cliente/servidor, a menudo puntos de entrada separados también. + - Menos isomórfico, diferentes ramas en tu aplicación para diferentes entornos. + +2. Frameworks / Envoltorios de Vite + - Ya no usan Vite directamente sino una abstracción. + - Cierta cantidad de compromiso/bloqueo (lock-in). + - La matriz de soporte para diferentes opciones de configuración de Vite, plugins, etc., puede ser complicada y menos que clara. + +Si bien estas soluciones tienen absolutamente sus méritos y lugares en el ecosistema, ninguna se sentía tan genial como podrían ser para nuestro ecosistema, dadas nuestras ofertas históricas en esta área. La DX (Experiencia de Desarrollador) del "mejor escenario" a menudo se sacrificaba por necesidades más complejas o específicas -- lo cual es un intercambio completamente válido. + +Para el prerenderizado "drop-in", sin embargo, pensamos que podíamos proporcionar algo un poco diferente a las opciones existentes, o al menos algo un poco más familiar para nuestros usuarios. + +## Implementación en `@preact/preset-vite` + +Años después, todavía estábamos bastante enamorados de la simplicidad y extensibilidad del prerenderizado de WMR y sentíamos que faltaba mucho en nuestro preset de Vite, así que buscamos portarlo con unos pocos ajustes menores para arreglar las dudas que teníamos. Un poco de trabajo después y ¡voilà, prerenderizado vía plugin! + +Para empezar, aquí hay un ejemplo de prerenderizado de una aplicación "Hola Mundo". + +> Pista: Nuestro inicializador de Vite, (`$ npm create preact`) puede configurar esto por ti junto con algunas otras opciones complementarias, como enrutamiento, TypeScript, etc. Si estás interesado en probar nuestro prerenderizado, es la forma más rápida de ponerte al día. + +En primer lugar, habilita el prerenderizado configurando `prerender: { enabled: true }` en las opciones del plugin `@preact/preset-vite`: + +```diff +// vite.config.js +import { defineConfig } from 'vite'; +import preact from '@preact/preset-vite'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + preact({ ++ prerender: { enabled: true } + }), + ], +}); +``` + +...luego agrega un atributo `prerender` al script que contiene nuestra función `prerender()` -- esto le permite al plugin saber dónde encontrarlo. Aunque puedes configurar esto en cualquier script que desees, para nuestros ejemplos aquí, siempre estará en la raíz de nuestra aplicación. + +```diff +// index.html +- ++ +``` + +...finalmente, haz un par de ajustes a la raíz de nuestra aplicación: + +1. Cambiar `render` a `hydrate` + + - `hydrate` de `preact-iso` es una utilidad muy pequeña que decide si renderizar la aplicación o hidratarla dependiendo de si puede encontrar marcado existente en el documento. En desarrollo usará `render`, pero en producción, con HTML prerenderizado, usará `hydrate`. + - Necesitamos agregar una verificación de ventana (`typeof window !== undefined`) para asegurar que no estamos intentando acceder a `document`, un global del navegador, en Node durante el SSR. + +2. Agregar nuestra exportación `prerender()` + - Este es el facilitador del prerenderizado, y es totalmente controlado por el usuario. Tú decides cómo debe renderizarse tu aplicación, qué props pasar a tu componente raíz, hacer cualquier ajuste al HTML, ejecutar cualquier post-procesamiento que desees, etc. Todo lo que el plugin necesita es que se devuelva un objeto conteniendo una propiedad `html` con tu cadena HTML. + - Para nuestros ejemplos aquí usaremos `prerender` de `preact-iso` que es un envoltorio delgado alrededor de `renderToStringAsync` de `preact-render-to-string` con una ventaja principal: recolecta y devuelve automáticamente los enlaces relativos que encuentra en las páginas que prerenderizas. El plugin de prerenderizado puede entonces usar estos enlaces para "recorrer" tu aplicación, descubriendo páginas por sí mismo. Mostraremos esto más adelante. + +```diff +// src/index.jsx +-import { render } from 'preact'; ++import { hydrate, prerender as ssr } from 'preact-iso'; + +function App() { + return

Hello World!

+} + +-render(, document.getElementById('app')); ++if (typeof window !== 'undefined') { ++ hydrate(, document.getElementById('app')); ++} + ++export async function prerender(data) { ++ return await ssr() ++} +``` + +Con esto configurado, tendrás una aplicación que se prerenderiza. Sin embargo, ninguna aplicación es realmente tan simple, así que veamos un par de ejemplos más complejos. + +### Ejemplo de API Completa + +```jsx +// src/index.jsx + +// ... + +export async function prerender(data) { + const { html, links: discoveredLinks } = ssr(); + + return { + html, + // Optionally add additional links that should be + // prerendered (if they haven't already been -- these will be deduped) + links: new Set([...discoveredLinks, '/foo', '/bar']), + // Optionally configure and add elements to the `` of + // the prerendered HTML document + head: { + // Sets the "lang" attribute: `` + lang: 'en', + // Sets the title for the current page: `My cool page` + title: 'My cool page', + // Sets any additional elements you want injected into the ``: + // + // + elements: new Set([ + { type: 'link', props: { rel: 'stylesheet', href: 'foo.css' } }, + { + type: 'meta', + props: { property: 'og:title', content: 'Social media title' } + } + ]) + } + }; +} +``` + +### Fetch de Contenido Isomórfico con Fetching basado en Suspense + +```jsx +// src/use-fetch.js +import { useState } from 'preact/hooks'; + +const cache = new Map(); + +async function load(url) { + const res = await fetch(url); + if (res.ok) return await res.text(); + throw new Error(`Failed to fetch ${url}!`); +} + +// Mecanismo de fetch basado en suspenso simple con Caching +export function useFetch(url) { + const [_, update] = useState({}); + + let data = cache.get(url); + if (!data) { + data = load(url); + cache.set(url, data); + data.then( + res => update((data.res = res)), + err => update((data.err = err)) + ); + } + + if (data.res) return data.res; + if (data.err) throw data.err; + throw data; +} +``` + +```jsx +// src/index.jsx +import { hydrate, prerender as ssr } from 'preact-iso'; +import { useFetch } from './use-fetch.js'; + +function App() { + return ( +
+ Loading...

}> +
+ +
+ ); +} + +function Article() { + const data = useFetch('/my-local-article.txt'); + return

{data}

; +} + +if (typeof window !== 'undefined') { + hydrate(, document.getElementById('app')); +} + +export async function prerender(data) { + return await ssr(); +} +``` + +### Usando `globalThis` para pasar datos + +```js +// src/title-util.js +import { useEffect } from 'preact/hooks'; + +/** + * Set `document.title` or `globalThis.title` + * @param {string} title + */ +export function useTitle(title) { + if (typeof window === 'undefined') { + globalThis.title = createTitle(title); + } + useEffect(() => { + if (title) { + document.title = createTitle(title); + } + }, [title]); +} +``` + +```jsx +// src/index.jsx +import { + LocationProvider, + Router, + hydrate, + prerender as ssr +} from 'preact-iso'; + +import { useTitle } from './title-util.js'; + +function App() { + return ( + +
+ + +
+
+ ); +} + +function Home() { + useTitle('Preact - Home'); + return

Hello World!

; +} + +function NotFound() { + useTitle('Preact - 404'); + return

Page Not Found

; +} + +if (typeof window !== 'undefined') { + hydrate(, document.getElementById('app')); +} + +export async function prerender(data) { + const { html, links } = await ssr(); + + return { + html, + links, + head: { + title: globalThis.title, + elements: new Set([ + { + type: 'meta', + props: { property: 'og:title', content: globalThis.title } + } + ]) + } + }; +} +``` + +Aunque es una necesidad menos común, también puedes usar una variación de este patrón para inicializar y pasar datos de prerenderizado profundamente en tu aplicación, omitiendo la necesidad de un store/contexto global. + +```jsx +// src/some/deep/Component.jsx +function MyComponent({ myFetchData }) { + const [myData, setMyData] = useState(myFetchData || 'some-fallback'); + ... +} +``` + +```js +let initialized = false; +export async function prerender(data) { + const init = async () => { + const res = await fetch(...); + if (res.ok) globalThis.myFetchData = await res.json(); + + initialized = true; + } + if (!initialized) await init(); + + const { html, links } = await ssr(); + ... +} +``` + +--- + +Para los curiosos que preguntan "¿Cómo funciona todo esto?", se puede desglosar en tres pasos simples: + +1. Configuración + + Configuramos el script con tu función `prerender()` exportada como una entrada adicional y le decimos a Rollup que preserve las firmas de entrada, permitiéndonos acceder y llamar a esa función después de la compilación. + +2. Compilación (Build) + + Dejamos que Vite compile tu aplicación como de costumbre: compilando JSX, ejecutando plugins, optimizando assets, etc. + +3. Prerenderizado + + Durante la etapa del plugin `generateBundle`, comenzamos a generar el HTML. Comenzando con `/`, empezamos a ejecutar los bundles JS compilados en Node, llamando a tu función `prerender()` e insertando el HTML que devuelve en tu documento `index.html`, finalmente escribiendo el resultado en el directorio de salida especificado. Cualquier enlace nuevo que tu función `prerender()` devuelva se encola para ser procesado a continuación. + + El prerenderizado se completa cuando nos quedamos sin URLs para retroalimentar a tu aplicación. + + Siguiendo esto, Vite continuará finalizando el proceso de compilación, ejecutando cualquier otro plugin que puedas tener. Tu aplicación prerenderizada estará entonces disponible inmediatamente, sin necesidad de compilaciones o scripts posteriores. + +### Algunas Características Geniales + +- Implementación de `fetch()` basada en sistema de archivos (como se muestra en el ejemplo de "Fetching Isomórfico") + - Antes de que corras a buscar tu antorcha, ¡escúchanos! Durante el prerenderizado (y solo durante el prerenderizado) parcheamos `fetch()` para permitir leer archivos directamente del sistema de archivos. Esto te permite consumir archivos estáticos (texto, JSON, Markdown, etc.) durante el prerenderizado sin tener que iniciar un servidor para consumirlo. Puedes usar las mismas rutas de archivo durante el prerenderizado que usarás en el navegador. + - De hecho, ¡así es como construimos la misma página que estás leyendo! `fetch('/content/blog/preact-prerender.json')`, que es lo que se activa cuando navegas a esta página, se traduce aproximadamente a `new Response(await fs.readFile('/content/blog/preact-prerender.json'))` durante el prerenderizado. Leemos el archivo, lo envolvemos en una `Response` para imitar una solicitud de red, y lo suministramos de vuelta a tu aplicación -- tu aplicación puede usar la misma solicitud `fetch()` durante el prerenderizado y en el cliente. - Combinar esto con suspense y una implementación de SSR asíncrona proporciona una DX realmente genial. +- Rastreo de Enlaces + - Parcialmente soportado por la exportación de la función `prerender()` proporcionada por el usuario, parcialmente por el plugin, puedes devolver un conjunto de enlaces al prerenderizar la página (`preact-iso` hace esto maravillosamente simple) que se agregarán a la lista de URLs del plugin para prerenderizar. Esto permitirá al plugin rastrear tu sitio en tiempo de compilación, encontrando más y más páginas para prerenderizar naturalmente. + - También puedes proporcionar enlaces manualmente a través de las opciones del plugin o adjuntando algunos a los que `preact-iso` devuelve, como mostramos arriba en el Ejemplo de API Completa. Esto es especialmente útil para páginas de error, como un `/404`, que podrían no estar enlazadas pero que aún así quieres tener prerenderizadas. + +...y quizás la mayor ventaja: + +- Alternarlo cambiando un Booleano en tu archivo de configuración + - Porque no somos un envoltorio, y porque no necesitas alterar tu código fuente para soportarlo (más allá de algunas verificaciones de ventana), no hay bloqueo (lock-in) alguno. Si decides alejarte, o quieres hacer algunas pruebas en tu salida, todo lo que necesitas hacer es cambiar un Booleano y vuelves a una SPA plana con Vite. + - Como hemos mencionado un par de veces, el prerenderizado es mejor cuando es lo más cercano posible a "drop-in" y eso incluye ser capaz de salir por capricho. Es importante para nosotros que puedas ir de una SPA al prerenderizado y viceversa con un esfuerzo mínimo. + +## Notas Finales + +Al equipo de Vite probablemente le gustaría que mencionáramos que este plugin introduce un pequeño parche al código del cliente generado, y que ellos (el equipo de Vite) no necesariamente respaldan la ejecución de los bundles del navegador en Node. + +El parche en cuestión es el siguiente: + +```diff +// src/node/plugins/importAnalysisBuild.ts +-if (__VITE_IS_MODERN__ && deps && deps.length > 0) {, ++if (__VITE_IS_MODERN__ && deps && deps.length > 0 && typeof window !== 'undefined') {, + const links = document.getElementsByTagName('link') + ... +``` + +Como intentar ejecutar `document.getElementsByTagName` dará error en Node donde no hay `document`, simplemente agregamos una condición adicional al precargador para que no intente ejecutarse en Node, y eso es todo. Solo el cambio parcial de esta línea que tendría poco propósito durante el prerenderizado de todos modos. + +Estamos muy, muy contentos con este nivel de riesgo y lo hemos estado usando intensamente durante algún tiempo sin ningún problema, pero, esto es de alguna manera usar la herramienta más allá de para lo que fue diseñada y es algo que queremos revelar. + +Para cualquier usuario que no sea de Preact, buenas noticias: ¡nuestro plugin es completamente agnóstico del framework! Para hacerlo ligeramente más fácil de usar en cualquier otro framework, esto se ofrece alternativamente como [`vite-prerender-plugin`](https://www.google.com/search?q=%5Bhttps://npm.im/vite-prerender-plugin%5D(https://npm.im/vite-prerender-plugin)). La misma funcionalidad, y mantenida en sincronía con `@preact/preset-vite`, pero elimina las otras utilidades específicas de Preact que se envían en el plugin del preset de Preact. From 7b1eedbfcf7244135ec4c0c18ebfd345c0164940 Mon Sep 17 00:00:00 2001 From: ezemastro Date: Sat, 14 Feb 2026 20:35:08 +0000 Subject: [PATCH 03/27] Add spanish translation for: simplifying-islands-arch.md --- content/es/blog/simplifying-islands-arch.md | 451 ++++++++++++++++++++ 1 file changed, 451 insertions(+) create mode 100644 content/es/blog/simplifying-islands-arch.md diff --git a/content/es/blog/simplifying-islands-arch.md b/content/es/blog/simplifying-islands-arch.md new file mode 100644 index 000000000..d22fa552a --- /dev/null +++ b/content/es/blog/simplifying-islands-arch.md @@ -0,0 +1,451 @@ +--- +title: Simplificando la arquitectura de Islas +date: 2024-10-27 +authors: + - reaper +--- + +> Esta es una versión ligeramente modificada del escrito original en https://barelyhuman.github.io/preact-islands-diy + +# Islas + +## Introducción + +Esta guía es un recorrido sencillo para entender cómo funciona la arquitectura de islas +y cómo poder configurar la tuya usando herramientas que ya tienes a mano. + +Antes que nada, ¿qué son las islas? Puedes leer más sobre su origen en + +[Islands Architecture - Jason Miller →](https://jasonformat.com/islands-architecture/) + +## ¿Por qué? + +Para muchos desarrolladores que han trabajado con renderizado en servidor por un +tiempo, esperábamos que la tecnología frontend tomara un giro hacia el renderizado +en servidor en algún momento, ya que la obtención y procesamiento de datos casi +siempre es más rápida en el servidor, donde estás más cerca de los datos. + +Esa es una de las muchas razones, pero hay otras sobre las que todo el mundo +está debatiendo, así que lo dejaremos a los expertos. + +Pasemos a implementar el concepto. + +# Empezando + +## Implementación básica + +La implementación básica se puede generalizar para la mayoría de las apps con +SSR + hidratación en cliente. + +Aquí tienes un resumen + +1. Renderizar inicialmente la vista en el servidor como una página estática. +2. Hidratar la app en el cliente + +Para entrar en los detalles de cada uno. + +### Renderizado inicial en el servidor + +En este paso, aún construyes el árbol de componentes con la librería UI que estés +usando, Vue, React, Preact, Solid, etc. Y luego aplanas el árbol de componentes para +quedarte solo con los datos estáticos e inmediatamente computables. En este caso, +no se ejecutan efectos secundarios ni código de gestión de estado. + +La salida es un documento HTML estático que puedes enviar al cliente. + +Ya que esta guía está ligada a [preact](https://preactjs.com/), vamos a usar +una librería del equipo de preact que nos ayuda a lograr esto. + +Aquí tienes cómo sería una implementación muy rudimentaria de renderizar un +componente en el servidor. + +Estamos usando `express.js` aquí como ejemplo debido a que es la primera elección de +muchos principiantes; el proceso es mayormente el mismo para cualquier otro motor de +servidor que elijas. Hapi, Koa, Fastify, etc. + +```js +// server.js +import { h } from 'preact'; +import preactRenderToString from 'preact-render-to-string'; + +// ...configuración restante de express.js + +const HomePage = () => { + return h('h1', {}, 'hello'); +}; + +app.get('/', async (req, res) => { + res.send(preactRenderToString(h(HomePage, {}))); +}); +``` + +Aquí la mayor parte del trabajo la hace `preactRenderToString`, y todo lo que +hacemos es escribir componentes. Con un poco de magia en el bundling, deberíamos +poder escribir en JSX para hacerlo un poco más amigable. + +### Hidratar + +Bien, un término que verás mucho por ahí. + +- Hidratación parcial +- Hidratación progresiva +- añadir más a medida que encuentren más formas + +Para decirlo simplemente, es enlazar la interactividad a un elemento del DOM con +estado/efectos/eventos _existentes_. + +Ese estado/efectos/eventos _existentes_ podría ser enviado desde el servidor, pero +si trabajas con un componente que puede manejar lo suyo y la lógica está bien +contenida, simplemente montas el componente en el DOM con las ligaduras necesarias. + +Como ejemplo, esto podría verse así + +```js +// client.js +import { hydrate } from 'preact'; +import Counter from './Counter'; + +const main = () => { + // suponiendo que el servidor también haya renderizado el componente con el siguiente ID. + const container = document.getElementById('counter'); + hydrate(h(Counter, {}), container); +}; + +main(); +``` + +Similar al paso de renderizado en servidor, usamos un helper de `preact` para ayudar +a hidratar un componente. Podrías usar `render`, pero como el elemento ya fue renderizado +por el servidor, renderizarlo otra vez no tendría sentido, así que le pedimos a preact +que intente añadir los eventos y el estado necesarios en su lugar. + +Lo que he explicado arriba se llama Hidratación Parcial, puesto que no hidratas la +app entera sino solo ciertas partes. + +## Profundizando + +No hay mucho más que necesites saber para entender cómo hacer una app basada en la +arquitectura de islas pero, ahora entremos en la implementación. + +# El Código + +La arquitectura a nivel de código para esto es muy similar a la mayoría de los modelos +SSR y Vite tiene una buena explicación de cómo escribir tu propio ssr con vite + +[→ Vite Guides - Server-Side Rendering](https://vitejs.dev/guide/ssr.html) + +Nosotros usamos webpack en su lugar, para hacerlo un poco más explícito y fácil de explicar. + +> Nota: Puedes obtener el código referenciado en [barelyhuman/preact-islands-diy](http://github.com/barelyhuman/preact-islands-diy/) + +## `server/app.js` + +Comenzando con el archivo `server/app.js`. Si tienes el código abierto localmente te será +útil al leer esto. + +El snippet de código abajo solo resalta las áreas necesarias + +```js +import preactRenderToString from 'preact-render-to-string'; +import HomePage from '../pages/HomePage.js'; +import { h } from 'preact'; +import { withManifestBundles } from '../lib/html.js'; + +const app = express(); + +app.get('/', async (req, res) => { + res.send( + withManifestBundles({ + body: preactRenderToString(h(HomePage, {})) + }) + ); +}); +``` + +Mirando las importaciones, tenemos las mismas importaciones mencionadas en la +sección [Comenzando](#getting-started) y no ha cambiado mucho. + +La única adición aquí es el helper `withManifestBundles` que es de lo que hablaremos a continuación. + +## `lib/html.js` + +El helper de HTML varía en las diferentes variantes de la plantilla, pero solo +recorreremos la versión de `webpack` que está en la rama `main`. + +El caso de uso base del helper es poder recorrer un JSON de manifest que lista +qué archivos están siendo bundleados por webpack y sus rutas con hash cuando se +usan en producción. + +Esto es requerido puesto que no sabremos el hash y necesitamos una forma programática +de averiguarlo. + +Este manifest es generado por la configuración cliente de webpack que revisaremos +en un momento. + +```js +// fetch el manifest de la salida del cliente +import manifest from '../../dist/js/manifest.json'; + +export const withManifestBundles = ({ styles, body }) => { + // recorrer cada clave del manifiesto y construir + // una etiqueta de script para cada una. + const bundledScripts = Object.keys(manifest).map(key => { + const scriptPath = `/public/js/${manifest[key]}`; + return ``; + }); + + return ` + + + + + + + + ${body} + + ${bundledScripts.join('')} + `; +}; +``` + +Como se explica en los comentarios, simplemente tomamos todos los archivos que +necesitamos desde el manifest e inyectamos etiquetas ` +``` + +[🔨 Edítelo en Glitch](https://glitch.com/~preact-no-build-tools) + +La desventaja principal de desarrollar de esta forma es la falta de JSX, que requiere un paso de compilación. Una alternativa ergonómica y performante a JSX se documenta en la siguiente sección. + +### Alternativas a JSX + +Escribir llamadas raw `h` o `createElement` puede ser tedioso. JSX tiene la ventaja de parecer similar a HTML, lo que lo hace más fácil de entender para muchos desarrolladores en nuestra experiencia. Sin embargo, JSX requiere un paso de compilación, por lo que altamente recomendamos una alternativa llamada [HTM][htm]. + +[HTM][htm] es una sintaxis similar a JSX que funciona en JavaScript estándar. En lugar de requerir un paso de compilación, utiliza la sintaxis de [Plantillas Etiquetadas](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Template_literals#plantillas_etiquetadas) propia de JavaScript, que fue añadida en 2015 y se soporta en [todos los navegadores modernos](https://caniuse.com/#feat=template-literals). Esta es una forma cada vez más popular de escribir aplicaciones Preact, ya que hay menos partes móviles que entender que en una configuración tradicional de herramientas de construcción frontend. + +```html + +``` + +[🔨 Edítelo en Glitch](https://glitch.com/~preact-with-htm) + +> **Consejo:** HTM también proporciona una versión conveniente de Preact con una única importación: +> +> `import { html, render } from 'https://esm.sh/htm/preact/standalone'` + +Para una solución más escalable, consulta [Mapas de Importación -- Uso Básico](/guide/v10/no-build-workflows#basic-usage), y para más información sobre HTM, consulta su [documentación][htm]. + +[htm]: https://github.com/developit/htm + +## Crear una aplicación Preact con Vite + +[Vite](https://vitejs.dev) se ha convertido en una herramienta increíblemente popular para construir aplicaciones en muchos frameworks en los últimos años, y Preact no es la excepción. Está construido sobre herramientas populares como ES modules, Rollup, y ESBuild. Vite, a través de nuestro inicializador o su plantilla de Preact, no requiere configuración o conocimiento previo para comenzar, y esta simplicidad lo hace una forma muy popular de usar Preact. + +Para empezar rápidamente con Vite, puedes usar nuestro inicializador `create-preact`. Esta es una aplicación de interfaz de línea de comandos (CLI) interactiva que se puede ejecutar en la terminal en tu máquina. Usándola, puedes crear una nueva aplicación ejecutando lo siguiente: + +```bash +npm init preact +``` + +Esto te guiará a través de la creación de una nueva aplicación Preact y te dará algunas opciones como soporte para TypeScript, enrutamiento (vía `preact-iso`), y soporte para ESLint. + +> **Consejo:** Ninguna de estas decisiones necesita ser final, siempre puedes añadir o eliminar de tu proyecto más tarde si cambias de opinión. + +### Listarse para el desarrollo + +Ahora estamos listos para iniciar nuestra aplicación. Para iniciar un servidor de desarrollo, ejecuta el siguiente comando dentro de tu carpeta de proyecto recién generada: + +```bash +# Ve a la carpeta del proyecto generado +cd my-preact-app + +# Inicia un servidor de desarrollo +npm run dev +``` + +Una vez que el servidor haya comenzado, imprimira una URL de desarrollo local para abrir en tu navegador. +¡Ahora estás listo para comenzar a codificar tu aplicación! + +### Hacer una compilación de producción + +Llega un momento en el que necesitas desplegar tu aplicación en algún lugar. Vite viene con un práctico comando `build` que generará una compilación de producción altamente optimizada. + +```bash +npm run build +``` + +Al completarse, tendrás una nueva carpeta `dist/` que se puede desplegar directamente en un servidor. + +> Para una lista completa de todos los comandos disponibles y sus opciones, consulta la [Documentación de la CLI de Vite](https://vitejs.dev/guide/cli.html). + +## Integrando en un Pipeline Existente + +Si ya tienes un pipeline de herramientas existente configurado, es muy probable que esto incluya un bundler. Las opciones más populares son [webpack](https://webpack.js.org/), [rollup](https://rollupjs.org) o [parcel](https://parceljs.org/). ¡Preact funciona de inmediato con todos ellos, sin cambios importantes necesarios! + +### Configurando JSX + +Para transpilar JSX, necesitas un plugin de Babel que lo convierta en código JavaScript válido. El que todos usamos es [@babel/plugin-transform-react-jsx](https://babeljs.io/docs/en/babel-plugin-transform-react-jsx). Una vez instalado, necesitas especificar la función para JSX que debe usarse: + +```json +{ + "plugins": [ + [ + "@babel/plugin-transform-react-jsx", + { + "pragma": "h", + "pragmaFrag": "Fragment" + } + ] + ] +} +``` + +> [Babel](https://babeljs.io/) tiene algunas de las mejores documentaciones por ahí. Altamente recomendamos consultarla para preguntas sobre Babel y cómo configurarlo. + +### Aliasing React a Preact + +En algún momento, probablemente querrás hacer uso del vasto ecosistema de React. Las librerías y Componentes originalmente escritos para React funcionan sin problemas con nuestra capa de compatibilidad. Para hacer uso de ella, necesitamos apuntar todas las importaciones de `react` y `react-dom` a Preact. Este paso se llama _aliasing_. + +> **Nota:** Si estás usando Vite (vía `@preact/preset-vite`), Preact CLI, o WMR, estos alias se manejan automáticamente para ti por defecto. + +#### Aliasing en Webpack + +Para hacer alias de cualquier paquete en Webpack, necesitas agregar la sección `resolve.alias` a tu configuración. Dependiendo de la configuración que uses, esta sección puede ya estar presente, pero faltando los alias para Preact. + +```js +const config = { + //... + resolve: { + alias: { + react: 'preact/compat', + 'react-dom/test-utils': 'preact/test-utils', + 'react-dom': 'preact/compat', // Debe estar debajo de test-utils + 'react/jsx-runtime': 'preact/jsx-runtime' + } + } +}; +``` + +#### Aliasing en Node + +Cuando se ejecuta en Node, los alias de bundler (Webpack, Rollup, etc.) no funcionan, como se puede ver en NextJS. Para arreglarlo, podemos usar alias directamente en nuestro `package.json`: + +```json +{ + "dependencies": { + "react": "npm:@preact/compat", + "react-dom": "npm:@preact/compat" + } +} +``` + +#### Aliasing en Parcel + +Parcel usa el archivo estándar `package.json` para leer opciones de configuración bajo una clave `alias`. + +```json +{ + "alias": { + "react": "preact/compat", + "react-dom/test-utils": "preact/test-utils", + "react-dom": "preact/compat", + "react/jsx-runtime": "preact/jsx-runtime" + } +} +``` + +#### Aliasing en Rollup + +Para hacer alias dentro de Rollup, necesitarás instalar [@rollup/plugin-alias](https://github.com/rollup/plugins/tree/master/packages/alias). El plugin necesitará ser colocado antes de tu [@rollup/plugin-node-resolve](https://github.com/rollup/plugins/tree/master/packages/node-resolve) + +```js +import alias from '@rollup/plugin-alias'; + +module.exports = { + plugins: [ + alias({ + entries: [ + { find: 'react', replacement: 'preact/compat' }, + { find: 'react-dom/test-utils', replacement: 'preact/test-utils' }, + { find: 'react-dom', replacement: 'preact/compat' }, + { find: 'react/jsx-runtime', replacement: 'preact/jsx-runtime' } + ] + }) + ] +}; +``` + +#### Aliasing en Jest + +[Jest](https://jestjs.io/) permite la reescritura de rutas de módulos similar a los bundlers. Estas reescrituras se configuran usando expresiones regulares en tu configuración de Jest: + +```json +{ + "moduleNameMapper": { + "^react$": "preact/compat", + "^react-dom/test-utils$": "preact/test-utils", + "^react-dom$": "preact/compat", + "^react/jsx-runtime$": "preact/jsx-runtime" + } +} +``` + +#### Aliasing en TypeScript + +TypeScript, incluso cuando se usa junto con un bundler, tiene su propio proceso de resolución de tipos. Para garantizar que se usen los tipos de Preact en lugar de los de React, querrás agregar la siguiente configuración a tu `tsconfig.json` (o `jsconfig.json`): + +```json +{ + "compilerOptions": { + ... + "skipLibCheck": true, + "baseUrl": "./", + "paths": { + "react": ["./node_modules/preact/compat/"], + "react/jsx-runtime": ["./node_modules/preact/jsx-runtime"], + "react-dom": ["./node_modules/preact/compat/"], + "react-dom/*": ["./node_modules/preact/compat/*"] + } + } +} +``` + +Además, es posible que desees habilitar `skipLibCheck` como lo hacemos en el ejemplo anterior. Algunas librerías de React usan tipos que pueden no ser proporcionados por `preact/compat` (aunque hacemos nuestro mejor para arreglar esto), y como tal, estas librerías podrían ser la fuente de errores de compilación de TypeScript. Al establecer `skipLibCheck`, puedes decirle a TS que no necesita hacer una verificación completa de todos los archivos `.d.ts` (generalmente limitados a tus librerías en `node_modules`) lo que corregirá estos errores. + +#### Aliasing con Mapas de Importación + +```html + +``` + +Consulta también [Mapas de Importación -- Recetas y Patrones Comunes](/guide/v10/no-build-workflows#recipes-and-common-patterns) para más ejemplos. From bec5c3e7c64cacca591798183500f0c9bf612d80 Mon Sep 17 00:00:00 2001 From: ezemastro Date: Sat, 14 Feb 2026 20:47:37 +0000 Subject: [PATCH 11/27] Add spanish translation for: guide/v11/hooks.md --- content/es/guide/v11/hooks.md | 510 ++++++++++++++++++++++++++++++++++ 1 file changed, 510 insertions(+) create mode 100644 content/es/guide/v11/hooks.md diff --git a/content/es/guide/v11/hooks.md b/content/es/guide/v11/hooks.md new file mode 100644 index 000000000..910f50f42 --- /dev/null +++ b/content/es/guide/v11/hooks.md @@ -0,0 +1,510 @@ +--- +title: Hooks +description: Los Hooks en Preact te permiten componer comportamientos juntos y reutilizar esa lógica en diferentes componentes +--- + +# Hooks + +La API de Hooks es un concepto nuevo que te permite componer estado y efectos secundarios. Los Hooks te permiten reutilizar la lógica con estado entre componentes. + +Si has trabajado con Preact por un tiempo, puedes estar familiarizado con patrones como "render props" y "componentes de orden superior" que intentan resolver estos desafíos. Estas soluciones han tendido a hacer que el código sea más difícil de seguir y más abstracto. La API de Hooks hace posible extraer claramente la lógica para estado y efectos secundarios, y también simplifica el testeo unitario de esa lógica independientemente de los componentes que se basan en ella. + +Los Hooks pueden ser usados en cualquier componente, y evitan muchas trampas de la palabra clave `this` en la que se basan los componentes de clase. En lugar de acceder a propiedades desde la instancia del componente, los Hooks se basan en cierres. Esto los hace vinculados a valores y elimina una serie de problemas de datos obsoletos que pueden ocurrir al tratar con actualizaciones de estado asincrónicas. + +Hay dos formas de importar hooks: desde `preact/hooks` o `preact/compat`. + +--- + + + +--- + +## Introducción + +La forma más fácil de entender los hooks es compararlos con Componentes equivalentes basados en clases. + +Usaremos un componente simple contador como nuestro ejemplo, que renderiza un número y un botón que lo aumenta en uno: + +```jsx +// --repl +import { render, Component } from 'preact'; +// --repl-before +class Counter extends Component { + state = { + value: 0 + }; + + increment = () => { + this.setState(prev => ({ value: prev.value + 1 })); + }; + + render(props, state) { + return ( +
+

Counter: {state.value}

+ +
+ ); + } +} +// --repl-after +render(, document.getElementById('app')); +``` + +Ahora, aquí está un componente de función equivalente construido con hooks: + +```jsx +// --repl +import { useState, useCallback } from 'preact/hooks'; +import { render } from 'preact'; +// --repl-before +function Counter() { + const [value, setValue] = useState(0); + const increment = useCallback(() => { + setValue(value + 1); + }, [value]); + + return ( +
+

Counter: {value}

+ +
+ ); +} +// --repl-after +render(, document.getElementById('app')); +``` + +En este punto parecen bastante similares, sin embargo podemos simplificar aún más la versión de hooks. + +Extrayamos la lógica del contador en un hook personalizado, haciéndolo fácilmente reutilizable en componentes: + +```jsx +// --repl +import { useState, useCallback } from 'preact/hooks'; +import { render } from 'preact'; +// --repl-before +function useCounter() { + const [value, setValue] = useState(0); + const increment = useCallback(() => { + setValue(value + 1); + }, [value]); + return { value, increment }; +} + +// Primer contador +function CounterA() { + const { value, increment } = useCounter(); + return ( +
+

Counter A: {value}

+ +
+ ); +} + +// Segundo contador, el cual renderiza otra salida. +function CounterB() { + const { value, increment } = useCounter(); + return ( +
+

Counter B: {value}

+

I'm a nice counter

+ +
+ ); +} +// --repl-after +render( +
+ + +
, + document.getElementById('app') +); +``` + +Ten en cuenta que tanto `CounterA` como `CounterB` son completamente independientes entre sí. Ambas usan el hook personalizado `useCounter()`, pero cada una tiene su propia instancia del estado asociado de ese hook. + +> ¿Piensas que esto se ve un poco extraño? ¡No eres el único! +> +> Para muchos de nosotros tardó un tiempo acostumbrarse a este enfoque. + +## El argumento de dependencia + +Muchos hooks aceptan un argumento que se puede usar para limitar cuándo se debe actualizar un hook. Preact inspecciona cada valor en un array de dependencias y verifica si ha cambiado desde la última vez que se llamó a un hook. Cuando el argumento de dependencia no se especifica, el hook siempre se ejecuta. + +En nuestra implementación de `useCounter()` anterior, pasamos un array de dependencias a `useCallback()`: + +```jsx +function useCounter() { + const [value, setValue] = useState(0); + const increment = useCallback(() => { + setValue(value + 1); + }, [value]); // <-- array de dependencias + return { value, increment }; +} +``` + +Pasar `value` aquí causa que `useCallback` devuelva una nueva referencia de función cada vez que `value` cambia. Esto es necesario para evitar "cierres obsoletos", donde la llamada siempre hacería referencia a la variable `value` del primer render desde cuando fue creada, causando que `increment` siempre establezca un valor de `1`. + +> Esto crea un nuevo callback `increment` cada vez que `value` cambia. Por razones de rendimiento, a menudo es mejor usar una [callback](#usestate) para actualizar valores de estado en lugar de retener el valor actual usando dependencias. + +## Hooks con Estado + +Aquí veremos cómo podemos introducir lógica con estado en componentes funcionales. + +Antes de la introducción de hooks, se requerían componentes de clase en cualquier lugar donde se necesitara estado. + +### useState + +Este hook acepta un argumento, este será el estado inicial. Cuando se invoca, este hook devuelve un array de dos variables. La primera siendo el estado actual y la segunda siendo el setter para nuestro estado. + +Nuestro setter se comporta similar al setter de nuestro estado clásico. Acepta un valor o una función con el currentState como argumento. + +Cuando llamas al setter y el estado es diferente, desencadenará un rerender comenzando desde el componente donde se ha usado ese useState. + +```jsx +// --repl +import { render } from 'preact'; +// --repl-before +import { useState } from 'preact/hooks'; + +const Counter = () => { + const [count, setCount] = useState(0); + const increment = () => setCount(count + 1); + // También puedes pasar un callback al setter. + const decrement = () => setCount(currentCount => currentCount - 1); + + return ( +
+

Count: {count}

+ + +
+ ); +}; +// --repl-after +render(, document.getElementById('app')); +``` + +> Cuando nuestro estado inicial es caro, es mejor pasar una función en lugar de un valor. + +### useReducer + +El hook `useReducer` tiene un gran parecido con [redux](https://redux.js.org/). Comparado con [useState](#usestate) es más fácil de usar cuando tienes lógica de estado compleja donde el siguiente estado depende del anterior. + +```jsx +// --repl +import { render } from 'preact'; +// --repl-before +import { useReducer } from 'preact/hooks'; + +const initialState = 0; +const reducer = (state, action) => { + switch (action) { + case 'increment': + return state + 1; + case 'decrement': + return state - 1; + case 'reset': + return 0; + default: + throw new Error('Unexpected action'); + } +}; + +function Counter() { + // Devuelve el estado actual y una función de envío para + // activar una acción. + const [count, dispatch] = useReducer(reducer, initialState); + return ( +
+ {count} + + + +
+ ); +} +// --repl-after +render(, document.getElementById('app')); +``` + +## Memoización + +En la programación de UI a menudo hay algo de estado o resultado que es caro de calcular. La Memoización puede cachear los resultados de ese cálculo permitiendo que sea reutilizado cuando se usa la misma entrada. + +### useMemo + +Con el hook `useMemo` podemos memoizar los resultados de ese cálculo y solo recalcularlo cuando una de las dependencias cambia. + +```jsx +const memoized = useMemo( + () => expensive(a, b), + // Solo vuelve a ejecutar la función costosa cuando alguna de estas + // dependencias cambie. + [a, b] +); +``` + +> No ejecutes ningún código con efectos secundarios dentro de `useMemo`. Los efectos secundarios pertenecen a `useEffect`. + +### useCallback + +El hook `useCallback` se puede usar para asegurar que la función devuelta permanezca referencialmente igual mientras no haya cambiado ninguna dependencia. Esto se puede usar para optimizar las actualizaciones de componentes secundarios cuando se basan en la igualdad referencial para omitir actualizaciones (p. ej. `shouldComponentUpdate`). + +```jsx +const onClick = useCallback(() => console.log(a, b), [a, b]); +``` + +> Hecho divertido: `useCallback(fn, deps)` es equivalente a `useMemo(() => fn, deps)`. + +## Refs + +Las **referencias** (refs para abreviar) son valores estables y locales que persisten entre rerenders pero no causan rerenders por sí mismos. Consulta [Refs](/guide/v10/refs) para más información y ejemplos. + +### useRef + +Para crear una referencia estable a un nodo del DOM o un valor que persista entre renders, podemos usar el hook `useRef`. Funciona de manera similar a [createRef](/guide/v10/refs#createref). + +```jsx +// --repl +import { useRef } from 'preact/hooks'; +import { render } from 'preact'; +// --repl-before +function Foo() { + // Inicializar useRef con un valor inicial de `null` + const input = useRef(null); + const onClick = () => input.current && input.current.focus(); + + return ( + <> + + + + ); +} +// --repl-after +render(, document.getElementById('app')); +``` + +> Ten cuidado de no confundir `useRef` con `createRef`. + +### useImperativeHandle + +Para mutar una ref que se pasa a un componente secundario podemos usar el hook `useImperativeHandle`. Toma tres argumentos: la ref a mutar, una función a ejecutar que devolverá el nuevo valor de ref, y un array de dependencias para determinar cuándo ejecutar de nuevo. + +```jsx +// --repl +import { render } from 'preact'; +import { useRef, useImperativeHandle, useState } from 'preact/hooks'; +// --repl-before +function MyInput({ inputRef }) { + const ref = useRef(null); + useImperativeHandle( + inputRef, + () => { + return { + // Solo exponer `.focus()`, no dar acceso directo al nodo DOM. + focus() { + ref.current.focus(); + } + }; + }, + [] + ); + + return ( + + ); +} + +function App() { + const inputRef = useRef(null); + + const handleClick = () => { + inputRef.current.focus(); + }; + + return ( +
+ + +
+ ); +} +// --repl-after +render(, document.getElementById('app')); +``` + +## useContext + +Para acceder a contexto en un componente funcional podemos usar el hook `useContext`, sin componentes wrapper o de orden superior. El primer argumento debe ser el objeto de contexto que se crea a partir de una llamada a `createContext`. + +```jsx +// --repl +import { render, createContext } from 'preact'; +import { useContext } from 'preact/hooks'; + +const OtherComponent = props => props.children; +// --repl-before +const Theme = createContext('light'); + +function DisplayTheme() { + const theme = useContext(Theme); + return

Active theme: {theme}

; +} + +// ...luego +function App() { + return ( + + + + + + ); +} +// --repl-after +render(, document.getElementById('app')); +``` + +## Efectos Secundarios + +Los Efectos Secundarios están en el corazón de muchas aplicaciones modernas. Ya sea que desees obtener algunos datos de una API o desencadenar un efecto en el documento, encontrarás que `useEffect` se adapta a casi todas tus necesidades. Es una de las principales ventajas de la API de hooks, que reformula tu mente en pensar en efectos en lugar del ciclo de vida de un componente. + +### useEffect + +Como el nombre lo implica, `useEffect` es la forma principal de desencadenar varios efectos secundarios. Incluso puedes devolver una función de limpieza desde tu efecto si es necesaria. + +```jsx +useEffect(() => { + // Dispara tu efecto + return () => { + // Opcional: cualquier código de limpieza. + }; +}, []); +``` + +Comenzaremos con un componente `Title` que debería reflejar el título al documento, para que podamos verlo en la barra de direcciones de nuestra pestaña en nuestro navegador. + +```jsx +function PageTitle(props) { + useEffect(() => { + document.title = props.title; + }, [props.title]); + + return

{props.title}

; +} +``` + +El primer argumento para `useEffect` es una devolusión de llamada sin argumentos que desencadena el efecto. En nuestro caso, solo queremos desencadenarlo cuando el título realmente ha cambiado. No habría punto en actualizarlo cuando se quede igual. Por eso estamos usando el segundo argumento para especificar nuestro [array de dependencia](#the-dependency-argument). + +Pero a veces tenemos un caso de uso más complejo. Piensa en un componente que necesita suscribirse a algunos datos cuando se monta y necesita desuscribirse cuando se desmonta. Esto también se puede lograr con `useEffect`. Para ejecutar cualquier código de limpieza solo necesitamos devolver una función en nuestra devolucIón de llamada. + +```jsx +// --repl +import { useState, useEffect } from 'preact/hooks'; +import { render } from 'preact'; +// --repl-before +// Componente que siempre mostrará el ancho actual de la ventana. +function WindowWidth(props) { + const [width, setWidth] = useState(0); + + function onResize() { + setWidth(window.innerWidth); + } + + useEffect(() => { + window.addEventListener('resize', onResize); + return () => window.removeEventListener('resize', onResize); + }, []); + + return

Window width: {width}

; +} +// --repl-after +render(, document.getElementById('app')); +``` + +> La función de limpieza es opcional. Si no necesitas ejecutar ningún código de limpieza, no necesitas devolver nada en la devolución de llamada que se pasa a `useEffect`. + +### useLayoutEffect + +La firma es idéntica a [useEffect](#useeffect), pero se disparará tan pronto como el componente sea diferenciado y el navegador tenga una oportunidad de pintar. + +### useErrorBoundary + +Cada vez que un componente secundario lanza un error, puedes usar este hook para capturarlo y mostrar una UI de error personalizada al usuario. + +```jsx +// error = El error que se detectó o `undefined` si no se produjo ningún error. +// resetError = Llame a esta función para marcar un error como resuelto. Depende +// de su aplicación decidir qué significa eso y si es posible +// recuperarse de los errores. +const [error, resetError] = useErrorBoundary(); +``` + +Para fines de monitoreo a menudo es increíblemente útil notificar a un servicio de cualquier error. Para eso podemos aprovechar una devolución de llamada opcional y pasar eso como el primer argumento a `useErrorBoundary`. + +```jsx +const [error] = useErrorBoundary(error => callMyApi(error.message)); +``` + +Un ejemplo de uso completo puede verse así: + +```jsx +const App = props => { + const [error, resetError] = useErrorBoundary(error => + callMyApi(error.message) + ); + + // Mostrar un mensaje de error agradable. + if (error) { + return ( +
+

{error.message}

+ +
+ ); + } else { + return
{props.children}
; + } +}; +``` + +> Si has estado usando la API de componentes basados en clases en el pasado, entonces este hook es esencialmente una alternativa al método de ciclo de vida [componentDidCatch](/guide/v10/whats-new/#componentdidcatch). +> Este hook fue introducido con Preact 10.2.0. + +## Hooks de Utilidad + +### useId + +Este hook generará un identificador único para cada invocación y garantiza que estos serán consistentes al renderizar tanto [en el servidor](/guide/v10/server-side-rendering) como en el cliente. Un caso de uso común para ID consistentes son los formularios, donde los elementos `