<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title><![CDATA[Frainer's Blog 📝]]></title>
        <description><![CDATA[Articulos recientes de Frainer's Blog. Escribo sobre tecnología, programación y cualquier otra cosa en la que esté pensando!]]></description>
        <link>https://www.fraineralex.dev/es/blog/</link>
        <image>
            <url>https://www.fraineralex.dev/images/blog/es-og.webp</url>
            <title>Frainer&apos;s Blog 📝</title>
            <link>https://www.fraineralex.dev/es/blog/</link>
        </image>
        <generator>RSS for Node</generator>
        <lastBuildDate>Sat, 07 Nov 2026 16:06:51 GMT</lastBuildDate>
        <atom:link href="https://www.fraineralex.dev/es/blog/rss.xml" rel="self" type="application/rss+xml"/>
        <pubDate>Thu, 18 Jan 2024 00:00:00 GMT</pubDate>
        <language><![CDATA[es-DO]]></language>
        <category><![CDATA[tecnología]]></category>
        <category><![CDATA[programación]]></category>
        <category><![CDATA[software]]></category>
        <dc:creator>Frainer Encarnación</dc:creator>
        <item>
            <title><![CDATA[La UI generativa se vuelve práctica con json-render y jev]]></title>
            <description><![CDATA[
    <header>
      <figure>
        <img
          alt="undefined"
          loading="lazy"
          decoding="async"
          data-nimg="1"
          src="https://www.fraineralex.dev/images/blog/posts/cover/jev-generative-ui-es.png"
          style="margin-top: 2rem; color: transparent; min-width: 80%; border-radius: .75rem; margin-left: auto; margin-right: auto; width: 100%; height: auto;"
        />
        <figcaption style=" text-align: right; font-size: 0.875rem; line-height: 1.25rem; padding-inline-end: 2rem; color: darkgray; padding-inline-end: 2rem; padding-top: 0.25rem;  font-style: italic;">
          
        </figcaption>
      </figure>
      <p style=" margin-left: auto; margin-right: auto; text-transform: uppercase; text-align: center; padding-bottom: 2rem;"
        ><span style="margin-bottom: 0;">
          <time datetime="2026-09-19"
            >Sep 19, 2026</time
          >
          <span style="padding-left: 1rem; padding-right: 1rem;">•</span
          ><span style="margin-inline-end: 0;">5 min de lectura</span>
          <span style="padding-left: 1rem; padding-right: 1rem;">|</span>
          
                  <a href="https://www.fraineralex.dev/tags/react" style="color: black; text-decoration-line: underline; text-underline-offset: 4px;">
                    React
                  </a>
                  <span style="padding-left: 10px; padding-right: 10px;"> • </span>
              
                  <a href="https://www.fraineralex.dev/tags/typescript" style="color: black; text-decoration-line: underline; text-underline-offset: 4px;">
                    TypeScript
                  </a>
                  <span style="padding-left: 10px; padding-right: 10px;"> • </span>
              
                  <a href="https://www.fraineralex.dev/tags/nextjs" style="color: black; text-decoration-line: underline; text-underline-offset: 4px;">
                    Next.js
                  </a>
                  <span style="padding-left: 10px; padding-right: 10px;"> • </span>
              
                  <a href="https://www.fraineralex.dev/tags/performance" style="color: black; text-decoration-line: underline; text-underline-offset: 4px;">
                    Web Perf
                  </a>
                  
              
          </span>
      </p>  
    </header>
  <article style="text-wrap: balance;"><p>La mayoría de los productos de IA todavía coloca el modelo dentro de una ventana de chat fija. Eso funciona para responder preguntas, pero se queda corto en flujos de reservas, listas de soporte o paneles operativos que necesitan tarjetas, formularios, tablas y acciones.</p>
<p>La UI generativa permite que el modelo ayude a componer la interfaz. La versión útil no genera píxeles sin control ni React que nadie revisó. Selecciona componentes y acciones de un <strong>catálogo</strong> que tu equipo ya mantiene.</p>
<p>En este artículo veremos <code>json-render</code> de Vercel Labs, la combinación experimental con <code>jev</code>, el papel de AI Gateway y una pequeña demo interactiva.</p>
<h2>Por qué jev está llamando la atención en Vercel AI Gateway</h2>
<p>Vercel informó que <strong>Jev fue el modelo adoptado más rápido en la historia de AI Gateway</strong>. Durante el primer día, TypeSafe AI llegó aproximadamente al <strong>13 % de los equipos</strong>, cerca de <strong>2 veces</strong> la familia GPT-5.6 y <strong>6 veces</strong> Fable 5.1.</p>
<img className="jev-tall-media" src="/images/blog/posts/content/vercel-jev-adoption-chart-es.webp" alt="Gráfico de Vercel AI Gateway: Jev tuvo la adopción más rápida en las primeras 24 horas" />
_Fuente: [Vercel en X](https://x.com/vercel)._ <p><strong>Jev es gratis en Vercel AI Gateway hasta el 25 de septiembre</strong>, una buena oportunidad para probar <code>typesafe-ai/jev</code> con composiciones reales.</p>
<img className="jev-tall-media" src="/images/blog/posts/content/vercel-jev-free-until-sep25-es.webp" alt="Promoción de Vercel AI Gateway: Jev gratis hasta el 25 de septiembre" />
_Fuente: [Vercel Developers en X](https://x.com/vercel_dev)._ <p>La adopción no equivale a estar listo para producción. Sí demuestra que existe demanda por modelos capaces de tomar decisiones estructuradas, no solo de escribir textos largos.</p>
<h2>Chat fijo frente a UI generativa</h2>
<pre><code class="language-text">Chat fijo:      solicitud -&gt; texto del LLM -&gt; burbuja de chat
UI generativa:  solicitud -&gt; elegir composición -&gt; renderizar componentes aprobados
</code></pre>
<p><img src="/images/blog/posts/content/jev-diagram-1-es.webp" alt="Diagrama: chat fijo frente a UI generativa"></p>
<p>La aplicación sigue siendo dueña de los componentes, el acceso a datos, los manejadores, la validación y los permisos. El catálogo le da al modelo espacio para componer sin entregarle el control del producto.</p>
<h2>Qué aporta json-render</h2>
<p><a href="https://json-render.dev/" target="_blank" rel="noopener noreferrer"><code>json-render</code></a> es un framework de UI generativa de Vercel Labs:</p>
<ol>
<li>Defines un <strong>catálogo</strong> con componentes, acciones y propiedades de Zod.</li>
<li>El modelo genera una <strong>especificación JSON</strong> limitada a ese catálogo.</li>
<li>Tus componentes reales de React, o React Native, la renderizan.</li>
<li><strong>SpecStream</strong> puede enviar parches JSONL para que la interfaz aparezca de forma progresiva.</li>
</ol>
<p><img src="/images/blog/posts/content/jev-diagram-2-es.webp" alt="Diagrama: flujo de json-render"></p>
<p>El catálogo es un límite del producto. La creatividad permanece dentro del sistema de diseño.</p>
<h2>Por qué las UI que generan árboles token a token pueden ser lentas</h2>
<p>Una salida estructurada clásica le pide a un modelo grande que escriba un árbol JSON completo token a token. En una UI, ese árbol puede ser ancho y profundo, lo que retrasa la primera renderización útil.</p>
<p>El experimento público de Chris Tate combinó <code>json-render</code> con <strong>jev</strong> de TypeSafe AI, un modelo de decisiones estructuradas, mediante <code>experimental_composeSpec</code> y <code>experimental_createEvaluator</code>.</p>
<BlogAutoplayVideo className="jev-tall-media rounded-xl border border-zinc-700 my-6" src="https://3fuapogza10xxil9.public.blob.vercel-storage.com/portfolio/blog/jev/ctatedev-jev-demo.mp4" />
_Demo de [Chris Tate en X](https://x.com/ctatedev/status/2101022101750571357)._ <p><img src="/images/blog/posts/content/jev-diagram-3-es.webp" alt="Diagrama: JSON de un LLM frente a evaluación con jev"></p>
<p>jev se concentra en <strong>qué</strong> componentes usar y <strong>dónde</strong> colocarlos. Tu renderer sigue dibujando la interfaz.</p>
<h2>Arquitectura con Vercel AI Gateway</h2>
<pre><code class="language-text">Cliente -&gt; tu API (servidor) -&gt; AI Gateway (typesafe-ai/jev) -&gt; Spec -&gt; React
</code></pre>
<p><img src="/images/blog/posts/content/jev-diagram-4-es.webp" alt="Diagrama: arquitectura con AI Gateway"></p>
<pre><code class="language-ts">import { experimental_createEvaluator } from &quot;@json-render/core&quot;;

const evaluate = experimental_createEvaluator({
  apiKey: process.env.AI_GATEWAY_API_KEY!,
  model: &quot;typesafe-ai/jev&quot;,
});
</code></pre>
<p>Trata las APIs <code>experimental_*</code> como versiones no publicadas y fija sus versiones. Confirma que Gateway permite TypeSafe AI en tu equipo. Valida cada Spec antes de renderizar y maneja los estados <code>finish</code>, <code>limit</code> y <code>unavailable</code>.</p>
<h2>Prueba una composición desde el catálogo</h2>
<p>El playground envía la instrucción a una ruta del servidor que compone una Spec con <strong>jev</strong> en Vercel AI Gateway. La clave de API permanece en el servidor y nunca entra en el bundle del navegador. Si la ruta no está disponible, el playground muestra el error dentro de la interfaz.</p>
<JevPlayground /><h2>Cuándo usar cada enfoque</h2>
<p><img src="/images/blog/posts/content/jev-diagram-5-es.webp" alt="Diagrama: cuándo usar cada enfoque"></p>
<table>
<thead>
<tr>
<th>Enfoque</th>
<th>Úsalo cuando</th>
</tr>
</thead>
<tbody><tr>
<td>Catálogo + jev / evaluator</td>
<td>Tienes un sistema de diseño y necesitas layouts dinámicos con límites claros</td>
</tr>
<tr>
<td>JSON estructurado con un LLM</td>
<td>Las Specs son pequeñas y la latencia es aceptable</td>
</tr>
<tr>
<td>Llamadas a herramientas</td>
<td>El modelo debe llamar APIs, no componer pantallas</td>
</tr>
<tr>
<td>Generación libre de código o píxeles</td>
<td>Solo estás explorando. Hay mucha libertad y también mucho riesgo</td>
</tr>
</tbody></table>
<h2>Lista antes de publicar</h2>
<ul>
<li>Mantén <code>AI_GATEWAY_API_KEY</code> solo en el servidor.</li>
<li>Confirma el acceso de Gateway a <code>typesafe-ai/jev</code>.</li>
<li>Valida el catálogo y la Spec con Zod.</li>
<li>Limita <code>maxElements</code>, <code>maxSteps</code> y <code>maxDepth</code>.</li>
<li>Ofrece una interfaz fija como alternativa.</li>
<li>Mide tu propia latencia y coste. No copies los tiempos de la demo.</li>
</ul>
<p>La UI generativa no reemplaza la ingeniería frontend. Hace que la calidad del catálogo, las acciones y la validación sean todavía más importantes. El modelo se convierte en una capa entre la intención y las capacidades que ya publicaste.</p>
<h2>Referencias</h2>
<ol>
<li><a href="https://json-render.dev/" target="_blank" rel="noopener noreferrer">json-render</a> y <a href="https://json-render.dev/docs/api/core" target="_blank" rel="noopener noreferrer">API de @json-render/core</a></li>
<li><a href="https://github.com/vercel-labs/json-render" target="_blank" rel="noopener noreferrer">vercel-labs/json-render</a></li>
<li><a href="https://x.com/ctatedev/status/2101022101750571357" target="_blank" rel="noopener noreferrer">Demo de Chris Tate: json-render + jev en X</a></li>
<li><a href="https://x.com/vercel" target="_blank" rel="noopener noreferrer">Vercel: Jev tuvo la adopción más rápida en AI Gateway</a></li>
<li><a href="https://x.com/vercel_dev" target="_blank" rel="noopener noreferrer">Vercel Developers: Jev gratis en AI Gateway hasta el 25 de septiembre</a></li>
</ol>
</article>]]></description>
            <link>https://www.fraineralex.dev/es/blog/jev-generative-ui</link>
            <guid isPermaLink="false">https://www.fraineralex.dev/es/blog/jev-generative-ui</guid>
            <category><![CDATA[react]]></category>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[nextjs]]></category>
            <category><![CDATA[performance]]></category>
            <dc:creator><![CDATA[Frainer Encarnación]]></dc:creator>
            <pubDate>Sat, 19 Sep 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://www.fraineralex.dev/es/blog/images/blog/posts/cover/rss/jev-generative-ui-es.png" length="0" type="png"/>
        </item>
        <item>
            <title><![CDATA[Conquistando los Hooks en React: Una Guía Completa]]></title>
            <description><![CDATA[
    <header>
      <figure>
        <img
          alt="undefined"
          loading="lazy"
          decoding="async"
          data-nimg="1"
          src="https://www.fraineralex.dev/images/blog/posts/cover/crafting-react-hooks.webp"
          style="margin-top: 2rem; color: transparent; min-width: 80%; border-radius: .75rem; margin-left: auto; margin-right: auto; width: 100%; height: auto;"
        />
        <figcaption style=" text-align: right; font-size: 0.875rem; line-height: 1.25rem; padding-inline-end: 2rem; color: darkgray; padding-inline-end: 2rem; padding-top: 0.25rem;  font-style: italic;">
          
        </figcaption>
      </figure>
      <p style=" margin-left: auto; margin-right: auto; text-transform: uppercase; text-align: center; padding-bottom: 2rem;"
        ><span style="margin-bottom: 0;">
          <time datetime="2024-01-24"
            >Jan 24, 2024</time
          >
          <span style="padding-left: 1rem; padding-right: 1rem;">•</span
          ><span style="margin-inline-end: 0;">19 min de lectura</span>
          <span style="padding-left: 1rem; padding-right: 1rem;">|</span>
          
                  <a href="https://www.fraineralex.dev/tags/react" style="color: black; text-decoration-line: underline; text-underline-offset: 4px;">
                    React
                  </a>
                  <span style="padding-left: 10px; padding-right: 10px;"> • </span>
              
                  <a href="https://www.fraineralex.dev/tags/javascript" style="color: black; text-decoration-line: underline; text-underline-offset: 4px;">
                    JavaScript
                  </a>
                  <span style="padding-left: 10px; padding-right: 10px;"> • </span>
              
                  <a href="https://www.fraineralex.dev/tags/typescript" style="color: black; text-decoration-line: underline; text-underline-offset: 4px;">
                    TypeScript
                  </a>
                  
              
          </span>
      </p>  
    </header>
  <article style="text-wrap: balance;"><p>En este artículo, revelaré de manera clara y simplificada cómo React Hooks puede hacer magia en tus proyectos, tal como me hubiera gustado aprender cuando los descubrí.</p>
<h3>🪝 Hooks: La magia de React</h3>
<p>En React, los hooks son utilidades que nos permiten ejecutar código arbitrario en nuestros componentes basados en acciones específicas. Los hooks proporcionan una forma de reutilizar la lógica de estado y efectos secundarios en componentes funcionales de manera más simple y declarativa.</p>
<blockquote>
<p><strong>🚨 Importante:</strong> Esta guía utiliza fragmentos de código <code>TypeScript</code> para ilustrar los conceptos. Si no estás familiarizado con TypeScript, siéntete libre de ignorar la escritura de tipos en los ejemplos.</p>
</blockquote>
<h2>🌱 Hooks Básicos</h2>
<p>Esta guía de referencia discutirá todos los hooks disponibles nativamente en React, pero primero, comencemos con los hooks básicos de React: <code>useState</code>, <code>useEffect</code> y <code>useContext</code>.</p>
<p><img src="https://fraineralex.dev/images/blog/posts/content/basic-hooks.webp" alt="Imagen de los hooks básicos de React"></p>
<h2>⚡ useState</h2>
<p>Es un hook que permite a los componentes funcionales gestionar y mantener su estado interno. Puedes usarlo para declarar variables de estado y acceder a ellas en tus componentes funcionales.</p>
<p><strong>La firma para el hook <code>useState</code> es la siguiente:</strong></p>
<pre><code class="language-tsx">const [state, setState] = useState(initialState)
</code></pre>
<p>Aquí, <code>state</code> y <code>setState</code> se refieren al valor del estado y a la función de actualización devuelta al invocar <code>useState</code> con algún <code>initialState</code>.</p>
<p>Es importante tener en cuenta que cuando tu componente se renderiza por primera vez e invoca <code>useState</code>, el <code>initialState</code> es el estado devuelto por <code>useState</code>.</p>
<p>Además, para actualizar el estado, la función de actualización del estado <code>setState</code> debe invocarse con un nuevo valor de estado, como se muestra a continuación:</p>
<pre><code class="language-tsx">setState(newValue)
</code></pre>
<p>Al hacer esto, se encola un nuevo renderizado del componente. <code>useState</code> garantiza que el valor del <code>state</code> siempre será el más reciente después de aplicar actualizaciones.</p>
<p><strong>Ejemplo de uso de <code>useState</code> en un componente React acerca de la tarjeta de seguimiento de X:</strong></p>
<pre><code class="language-tsx">export function XFollowCard({ fullName, username, initialFollowing }: Props) {
  const [following, setFollowing] = useState(initialFollowing) // &lt;-- Definición

  const text = following ? &#39;Siguiendo&#39; : &#39;Seguir&#39;

  const buttonClassName = following
    ? &#39;tw-followCard-button is-following&#39;
    : &#39;tw-followCard-button&#39;

  const handleClick = () =&gt; {
    setFollowing(!following) // Cambiando el estado
  }

  return (
    &lt;article className=&#39;tw-followCard&#39;&gt;
      &lt;header className=&#39;tw-followCard-header&#39;&gt;
        &lt;img
          className=&#39;tw-followCard-avatar&#39;
          alt=&#39;user avatar&#39;
          src={`https://unavatar.io/${username}`}
        /&gt;
        &lt;div className=&#39;tw-followCard-info&#39;&gt;
          &lt;strong&gt;{fullName}&lt;/strong&gt;
          &lt;span className=&#39;tw-followCard-infoUserName&#39;&gt;@{username}&lt;/span&gt;
        &lt;/div&gt;
      &lt;/header&gt;

      &lt;aside&gt;
        &lt;button className={buttonClassName} onClick={handleClick}&gt;
          &lt;span className=&#39;tw-followCard-text&#39;&gt;{text}&lt;/span&gt;
          &lt;span className=&#39;tw-followCard-stopFollow&#39;&gt;Dejar de seguir&lt;/span&gt;
        &lt;/button&gt;
      &lt;/aside&gt;
    &lt;/article&gt;
  )
}
</code></pre>
<h2>🎬 useEffect</h2>
<p>Es un hook de React que permite realizar efectos secundarios en componentes funcionales. Puedes usarlo para ejecutar código en respuesta a cambios en el componente, como hacer solicitudes a API, suscribirte a eventos o actualizar el DOM. <code>useEffect</code> recibe una función como argumento y la ejecuta después de que el componente se renderiza o cuando ciertas dependencias cambian.</p>
<p>La firma básica de <code>useEffect</code> es la siguiente:</p>
<pre><code class="language-tsx">useEffect(() =&gt; {
  //...
})
</code></pre>
<p><strong>Ejemplo de uso de <code>useEffect</code> en un componente React:</strong></p>
<pre><code class="language-tsx">const CAT_IMAGE_URL_PREFIX = &#39;https://cataas.com&#39;

export function useCatImage({ fact }: { fact: string }) {
  const [imageUrl, setImageUrl] = useState()

  useEffect(() =&gt; {
    if (!fact) return

    const firstThreeWords = fact.split(&#39; &#39;, 3).join(&#39; &#39;)

    fetch(
      `${CAT_IMAGE_URL_PREFIX}/cat/says/${firstThreeWords}?size=50&amp;color=red&amp;json=true`
    )
      .then((res) =&gt; res.json())
      .then((response) =&gt; {
        const { url } = response
        setImageUrl(url)
      })
  }, [fact])

  return { imageUrl: `${CAT_IMAGE_URL_PREFIX}${imageUrl}` }
}
</code></pre>
<h3>🧹 Limpiando un efecto</h3>
<p>Algun código imperativo necesita ser limpiado para prevenir fugas de memoria. Por ejemplo, las suscripciones deben ser limpiadas, los temporizadores deben ser invalidados, etc. Para hacer esto, se retorna una función desde el callback pasado a <code>useEffect</code>:</p>
<pre><code class="language-tsx">useEffect(() =&gt; {
  const subscription = props.apiSubscription()

  return () =&gt; {
    // limpiar la suscripción
    subscription.unsubscribeApi()
  }
})
</code></pre>
<p>Se garantiza que la función de limpieza se invoque antes de que el componente sea eliminado de la interfaz de usuario.</p>
<h3>⚠️ Evitar bucles infinitos</h3>
<p>Cuando se utiliza <code>useEffect</code> en React, es crucial evitar bucles infinitos que pueden surgir de configuraciones incorrectas del efecto. Un error común es modificar el estado dentro del cuerpo del efecto de una manera que desencadene renderizaciones infinitas.</p>
<blockquote>
<p><strong>Causa común de bucles infinitos:</strong> Modificación directa del estado</p>
</blockquote>
<pre><code class="language-tsx">useEffect(() =&gt; {
  // Incorrecto: Modificar el estado dentro del cuerpo del efecto
  setCounter(counter + 1)
}, [counter])
</code></pre>
<p>Este ejemplo provoca un bucle infinito al actualizar el estado dentro del efecto, desencadenando nuevas renderizaciones y reejecutando el efecto.</p>
<h3>✅ Solución</h3>
<pre><code class="language-tsx">useEffect(() =&gt; {
  // Correcto: Usa el callback the setCount para actualizar el estado basándo en el estado anterior.
  // Esto asegura que la actualización del estado no desencadene un bucle infinito.
  setCount(prevCount =&gt; prevCount + 1)
}, [count])
</code></pre>
<p>Evita bucles infinitos asegurándote de que las dependencias en el array de dependencias del <code>useEffect</code> son estables y no cambian dentro del cuerpo del efecto. En este caso, al utilizar el callback de <code>setCount</code> para actualizar el estado basándose en el estado anterior, evitamos el bucle infinito.</p>
<h2>🤝 useContext</h2>
<p>El problema que resuelve <code>useContext</code> radica en la necesidad de pasar datos a través de la jerarquía de componentes sin recurrir al prop drilling, que es cuando se pasan props manualmente a través de varios niveles de componentes. Este enfoque puede volverse engorroso e impráctico a medida que la aplicación crece, ya que cada componente intermedio tiene que transmitir las props, generando un código menos limpio y propenso a errores.</p>
<h3>❌ Prop Drilling: El Desafío</h3>
<p>Imagina un escenario en el que tenemos un <code>ComponentA</code> que necesita pasar datos a <code>ComponentD</code>. El prop drilling tradicional implicaría pasar las props a través de todos los componentes intermedios, incluso si <code>ComponentB</code> y <code>ComponentC</code> no necesitan esos datos.</p>
<pre><code class="language-tsx">// ComponentA.tsx
const ComponentA = ({ dataForD }: Props) =&gt; {
  return &lt;ComponentB dataForD={dataForD} /&gt;
}

// ComponentB.tsx
const ComponentB = ({ dataForD }: Props) =&gt; {
  return &lt;ComponentC dataForD={dataForD} /&gt;
}

// ComponentC.tsx
const ComponentC = ({ dataForD }: Props) =&gt; {
  return &lt;ComponentD dataForD={dataForD} /&gt;
}

// ComponentD.tsx
const ComponentD = ({ dataForD }: Props) =&gt; {
  // Todo para usar el dataForD aquí
}
</code></pre>
<p>Este proceso, conocido como prop drilling, puede volverse inmanejable y complicado a medida que se agregan más componentes a la jerarquía.</p>
<h3>✅ Solución: ¡<code>useContext</code> al Rescate!</h3>
<p><code>useContext</code> simplifica en gran medida esta tarea al permitirnos crear y consumir un contexto sin pasar manualmente las props a través de cada componente. Primero, creamos un contexto usando <code>createContext</code>:</p>
<pre><code class="language-tsx">// ThemeContext.ts
import { createContext } from &#39;react&#39;
export const ThemeContext = createContext()
</code></pre>
<p>Luego, usamos el componente <code>Provider</code> para envolver nuestro árbol de componentes con el valor deseado:</p>
<pre><code class="language-tsx">// App.tsx
import React from &#39;react&#39;
import { ThemeContext } from &#39;./ThemeContext&#39;

const App = () =&gt; {
  return (
    &lt;ThemeContext.Provider value=&#39;dark&#39;&gt;
      {/* Componentes anidados aquí */}
    &lt;/ThemeContext.Provider&gt;
  )
}
</code></pre>
<p>Finalmente, en cualquier componente dentro de este contexto, podemos usar <code>useContext</code> para acceder a los datos sin pasar manualmente las props:</p>
<pre><code class="language-tsx">// Button.tsx
import React, { useContext } from &#39;react&#39;
import { ThemeContext } from &#39;./ThemeContext&#39;

export const Button = () =&gt; {
  const theme = useContext(ThemeContext)
  return &lt;button className={theme}&gt;Haz clic aquí&lt;/button&gt;
}
</code></pre>
<blockquote>
<p>Ten en cuenta que el valor pasado a <code>useContext</code> debe ser el objeto de contexto, es decir, el valor devuelto al invocar <code>React.createContext</code>, no <code>ContextObject.Provider</code> o <code>ContextObject.Consumer</code>.</p>
</blockquote>
<p>Esta solución evita el engorroso prop drilling y proporciona una forma más limpia y eficiente de compartir datos entre componentes en una aplicación de React. <code>useContext</code> hace que la gestión de datos sea más elegante y fácil de mantener.</p>
<p>¡Adiós, prop drilling! 👋</p>
<h2>🌟 Hooks Adicionales</h2>
<p>Los siguientes hooks son variantes de los hooks básicos discutidos en las secciones anteriores. Si eres nuevo en los hooks, no te molestes en aprender estos por ahora. Solo son necesarios para casos específicos.</p>
<p><img src="https://fraineralex.dev/images/blog/posts/content/additional-hooks.webp" alt="Imagen de hooks adicionales de React"></p>
<h2>📌 useRef</h2>
<p><code>useRef</code> en React es un hook que facilita la creación de referencias mutables. A diferencia de <code>useState</code>, <code>useRef</code> no desencadena la actualización de componentes cuando su valor cambia, convirtiéndolo en una herramienta poderosa para almacenar datos que no afectan directamente la interfaz de usuario.</p>
<p>Así es como se utiliza el hook <code>useRef</code>:</p>
<pre><code class="language-tsx">import { useRef } from &#39;react&#39;

const MyComponent = () =&gt; {
  const myRef = useRef(initialValue)
  // ...
}
</code></pre>
<h3>🥷🏻 Desafío</h3>
<p>El problema que resuelve <code>useRef</code> se hace evidente cuando necesitamos actualizar valores sin activar una actualización visual del componente. Para ilustrar esto, consideremos un escenario de búsqueda de películas:</p>
<pre><code class="language-tsx">export function useMovies({ search }: { search: string }) {
  const [movies, setMovies] = useState([])
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState(null)

  const getMovies = async () =&gt; {
    // ❌ Esto es una mala práctica porque estamos obteniendo
    // las películas y volviendo a renderizar el componente
    // incluso si la búsqueda es la misma que la anterior.

    try {
      setLoading(true)
      setError(null)
      const newMovies = await searchMovies({ search })
      setMovies(newMovies)
    } catch (error: any) {
      setError(error.message)
    } finally {
      setLoading(false)
    }
  }

  return { movies, getMovies, loading, error }
}
</code></pre>
<p>En este caso, usar <code>useState</code> para el término de búsqueda provoca renderizaciones innecesarias cada vez que cambia el término de búsqueda. Aquí es donde <code>useRef</code> se convierte en una solución eficiente al permitirnos actualizar <code>search</code> sin afectar la interfaz de usuario:</p>
<pre><code class="language-tsx">export function useMovies({ search }: { search: string }) {
  const [movies, setMovies] = useState([])
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState(null)
  const previousSearch = useRef(search) // &lt;-- Define una ref

  const getMovies = async () =&gt; {
    // ✅ Si la búsqueda actual es igual a la anterior, retorna
    if (previousSearch.current === search) return

    try {
      setLoading(true)
      setError(null)
      previousSearch.current = search
      const newMovies = await searchMovies({ search })
      setMovies(newMovies)
    } catch (error: any) {
      setError(error.message)
    } finally {
      setLoading(false)
    }
  }

  return { movies, getMovies, loading, error }
}
</code></pre>
<p>Con esta implementación, <code>useRef</code> ayuda a gestionar el valor de <code>search</code> sin causar actualizaciones visuales no deseadas, mejorando la eficiencia y la experiencia del usuario.</p>
<h2>🧠 useMemo</h2>
<p>El hook <code>useMemo</code> en React es una herramienta poderosa para optimizar el rendimiento al memorizar el resultado de una función. Su propósito principal es evitar cálculos innecesarios, reutilizando el valor calculado anteriormente si las dependencias no han cambiado.</p>
<p>Así es como se utiliza el hook <code>useMemo</code>:</p>
<pre><code class="language-tsx">const readTime = useMemo(() =&gt; {
  const wordsPerMinute = 200
  const words = text.trim().split(/\s+/).length
  const value = Math.ceil(words / wordsPerMinute)
  return `${value} min de lectura`
}, [text]) // solo recalcular si cambia el texto
</code></pre>
<p>El problema que aborda <code>useMemo</code> se hace evidente cuando necesitamos realizar cálculos costosos en un componente, pero esos cálculos no son necesarios cada vez que el componente se renderiza. Tomemos el ejemplo del componente <code>Counter</code>:</p>
<pre><code class="language-tsx">export function Counter({ count }: { count: number }) {
  const double = count * 2 // Costoso cálculo realizado en cada renderización

  return (
    &lt;div&gt;
      &lt;p&gt;Contador: {count}&lt;/p&gt;
      &lt;p&gt;Doble: {double}&lt;/p&gt;
    &lt;/div&gt;
  )
}
</code></pre>
<p>En este caso, el valor doble se recalcula en cada renderización, incluso si <code>count</code> no ha cambiado. <code>useMemo</code> resuelve esto al memorizar el resultado del cálculo solo cuando las dependencias, en este caso, la propiedad <code>count</code>, han cambiado:</p>
<pre><code class="language-tsx">import { useMemo } from &#39;react&#39;

export function Counter({ count }: { count: number }) {
  const double = useMemo(() =&gt; count * 2, [count])

  return (
    &lt;div&gt;
      &lt;p&gt;Contador: {count}&lt;/p&gt;
      &lt;p&gt;Doble: {double}&lt;/p&gt;
    &lt;/div&gt;
  )
}
</code></pre>
<p>Con <code>useMemo</code>, si la propiedad <code>count</code> no ha cambiado, se evita el recálculo del doble y se reutiliza el valor calculado anteriormente. Esto mejora significativamente la eficiencia del componente, especialmente en situaciones donde los cálculos son más intensivos en recursos.</p>
<h2>♻️ useCallback</h2>
<p>El hook <code>useCallback</code> en React proporciona una forma de memorizar funciones. La principal ventaja es evitar la creación de nuevas funciones en cada renderización, devolviendo la función memorizada previamente si las dependencias no han cambiado.</p>
<p>Así es como se utiliza el hook <code>useCallback</code>:</p>
<pre><code class="language-tsx">const callbackMemorizado = useCallback(callback, arrayDependencia)
</code></pre>
<p>El problema que aborda <code>useCallback</code> se hace evidente cuando necesitamos pasar funciones a componentes hijos y queremos evitar la creación de nuevas instancias de esas funciones en cada renderización. Tomemos el ejemplo del componente <code>Counter</code>:</p>
<pre><code class="language-tsx">function Counter({ count, onIncrement }: Props) {
  const handleIncrement = () =&gt; {
    onIncrement(count)
  }

  return (
    &lt;div&gt;
      &lt;p&gt;Contador: {count}&lt;/p&gt;
      &lt;button onClick={handleIncrement}&gt;Incrementar&lt;/button&gt;
    &lt;/div&gt;
  )
}
</code></pre>
<p>En este caso, <code>handleIncrement</code> se recrea en cada renderización, lo cual no es eficiente, especialmente si <code>onIncrement</code> y <code>count</code> no han cambiado. <code>useCallback</code> aborda esto al memorizar la función solo cuando cambian las dependencias:</p>
<pre><code class="language-tsx">import { useCallback } from &#39;react&#39;

export function Counter({ count, onIncrement }: Props) {
  const handleIncrement = useCallback(() =&gt; {
    onIncrement(count)
  }, [count, onIncrement

]) // solo recrear si count u onIncrement cambian

  return (
    &lt;div&gt;
      &lt;p&gt;Contador: {count}&lt;/p&gt;
      &lt;button onClick={handleIncrement}&gt;Incrementar&lt;/button&gt;
    &lt;/div&gt;
  )
}
</code></pre>
<p>Con <code>useCallback</code>, si <code>count</code> o <code>onIncrement</code> no han cambiado, se evita la creación de una nueva función y se reutiliza la función calculada previamente. Esto mejora la eficiencia y contribuye a un rendimiento del componente más óptimo.</p>
<h2>🏷️ useId</h2>
<p><code>useId</code> es un hook de React diseñado para generar identificadores únicos, ideales para asignarlos a atributos de etiquetas HTML. Esta práctica resulta especialmente útil para mejorar la accesibilidad al establecer relaciones específicas entre elementos.</p>
<p>Así es como se utiliza el hook <code>useId</code>:</p>
<pre><code class="language-tsx">const passwordHintId = useId()
</code></pre>
<p>A continuación, el ID generado se utiliza en diferentes atributos:</p>
<pre><code class="language-tsx">&lt;&gt;
  &lt;input type=&#39;password&#39; aria-describedby={passwordHintId} /&gt;
  &lt;p id={passwordHintId}&gt;
&lt;/&gt;
</code></pre>
<p>El problema que aborda <code>useId</code> surge cuando necesitamos asignar identificadores únicos a elementos HTML, especialmente en situaciones en las que un componente puede aparecer múltiples veces en la pantalla. Tomemos el ejemplo del componente <code>PasswordField</code>:</p>
<pre><code class="language-tsx">import { useId } from &#39;react&#39;

export function PasswordField() {
  const passwordHintId = useId()

  return (
    &lt;&gt;
      &lt;label&gt;
        Contraseña:
        &lt;input type=&#39;password&#39; aria-describedby={passwordHintId} /&gt;
      &lt;/label&gt;
      &lt;p id={passwordHintId}&gt;
        La contraseña debe tener 18 caracteres y contener caracteres especiales.
      &lt;/p&gt;
    &lt;/&gt;
  )
}
</code></pre>
<p>En este caso, <code>useId</code> se utiliza para generar un ID único (<code>passwordHintId</code>) que se asigna tanto al atributo <code>aria-describedby</code> del input como al <code>id</code> del párrafo. Esto asegura que aunque <code>PasswordField</code> aparezca varias veces en la pantalla, los IDs generados no entrarán en conflicto.</p>
<pre><code class="language-tsx">export default function App() {
  return (
    &lt;&gt;
      &lt;h2&gt;Elegir contraseña&lt;/h2&gt;
      &lt;PasswordField /&gt;
      &lt;h2&gt;Confirmar contraseña&lt;/h2&gt;
      &lt;PasswordField /&gt;
    &lt;/&gt;
  )
}
</code></pre>
<p>En el componente <code>App</code>, donde se usa <code>PasswordField</code> dos veces, <code>useId</code> asegura que los identificadores generados automáticamente eviten duplicados y proporcionen una solución robusta para asignar IDs únicos en contextos repetidos. Esto contribuye a mejorar la accesibilidad y consistencia en la aplicación.</p>
<h2>🕹️ useReducer</h2>
<p><code>useReducer</code> es un hook de React diseñado para gestionar el estado de un componente mediante un enfoque basado en acciones y reducciones, similar al patrón utilizado en Redux. Este hook se prefiere sobre <code>useState</code> cuando el estado de un componente es más complejo o cuando las actualizaciones de estado dependen del estado anterior o de acciones previas.</p>
<p><code>useReducer</code> toma dos argumentos: una función de reducción y el estado inicial. La función de reducción recibe dos argumentos: el estado actual y una acción que describe cómo debería cambiar el estado. La función de reducción devuelve el nuevo estado.</p>
<p><strong>Así es como se utiliza el hook <code>useReducer</code>:</strong></p>
<pre><code class="language-tsx">const [state, dispatch] = useReducer(reducer, argumentoInicial, init)
</code></pre>
<p>El problema que aborda <code>useReducer</code> surge cuando el estado de un componente se vuelve más complejo y las actualizaciones de estado dependen de múltiples factores o acciones. Tomemos el ejemplo del componente <code>Counter</code>:</p>
<pre><code class="language-tsx">import { useState } from &#39;react&#39;

export const Counter = () =&gt; {
  const [count, setCount] = useState(0)

  const handleIncrement = () =&gt; {
    setCount(count + 1)
  }

  const handleDecrement = () =&gt; {
    setCount(count - 1)
  }

  return (
    &lt;div&gt;
      &lt;p&gt;Contador: {count}&lt;/p&gt;
      &lt;button onClick={handleIncrement}&gt;Incrementar&lt;/button&gt;
      &lt;button onClick={handleDecrement}&gt;Decrementar&lt;/button&gt;
    &lt;/div&gt;
  )
}
</code></pre>
<p>En este ejemplo, usamos <code>useState</code> para gestionar el estado del contador. Si bien esto puede funcionar para casos simples, a medida que la lógica del estado se vuelve más compleja o depende de acciones previas, el código puede volverse más difícil de mantener. <code>useReducer</code> proporciona una estructura más organizada y una solución más escalable para manejar estados y acciones complejas en situaciones donde el código podría volverse confuso usando solo <code>useState</code>. Abordaríamos este problema con <code>useReducer</code> de la siguiente manera:</p>
<pre><code class="language-tsx">// Define la función de reducción
const counterReducer = (state: State, action: Action) =&gt; {
  switch (action.type) {
    case &#39;incrementar&#39;:
      return { count: state.count + 1 }
    case &#39;decrementar&#39;:
      return { count: state.count - 1 }
    default:
      return state
  }
}

export const Counter = () =&gt; {
  // Usa useReducer para gestionar el estado del contador
  const [state, dispatch] = useReducer(counterReducer, { count: 0 })

  return (
    &lt;div&gt;
      &lt;p&gt;Contador: {state.count}&lt;/p&gt;
      &lt;button onClick={() =&gt; dispatch({ type: &#39;incrementar&#39; })}&gt;Incrementar&lt;/button&gt;
      &lt;button onClick={() =&gt; dispatch({ type: &#39;decrementar&#39; })}&gt;Decrementar&lt;/button&gt;
    &lt;/div&gt;
  )
}
</code></pre>
<h2>✨ useImperativeHandle</h2>
<p><code>useImperativeHandle</code> en React es una herramienta útil cuando necesitas controlar y personalizar la interfaz externa de un componente funcional, especialmente al trabajar con refs. Imagina el desafío al que te enfrentarías sin este gancho.</p>
<h3>🤔 Desafío Sin <code>useImperativeHandle</code>:</h3>
<p>Supongamos que tenemos un componente funcional llamado <code>FancyInput</code> que encapsula un campo de texto. Queremos que el componente padre acceda y manipule el valor del campo directamente y también active el enfoque en ese campo. Sin <code>useImperativeHandle</code>, podríamos terminar con un código menos eficiente y propenso a errores.</p>
<pre><code class="language-tsx">export const FancyInput = () =&gt; {
  const inputRef = useRef&lt;HTMLInputElement&gt;(null)

  const focusInput = () =&gt; {
    if (inputRef.current) {
      inputRef.current.focus()
    }
  }

  return (
    &lt;div&gt;
      &lt;input ref={inputRef} /&gt;
      {/* Desafío: ¿Cómo exponer el método `focusInput` al componente padre? */}
    &lt;/div&gt;
  )
}
</code></pre>
<p>En este escenario, exponer el método <code>focusInput</code> al componente padre sería una tarea desafiante e inelegante. Aquí es donde entra en juego <code>useImperativeHandle</code>.</p>
<h3>✅ Solución con <code>useImperativeHandle</code>:</h3>
<pre><code class="language-tsx">export const FancyInput = forwardRef((props, ref) =&gt; {
  const inputRef = useRef&lt;HTMLInputElement&gt;(null)

  // Usa useImperativeHandle para personalizar la interfaz externa
  useImperativeHandle(ref, () =&gt; ({
    focus: () =&gt; {
      if (inputRef.current) {
        inputRef.current.focus()
      }
    },
    getValue: () =&gt; {
      return inputRef.current ? inputRef.current.value : &#39;&#39;
    },
  }))

  return (
    &lt;div&gt;
      &lt;input ref={inputRef} /&gt;
    &lt;/div&gt;
  )
})
</code></pre>
<p>Con <code>useImperativeHandle</code>, ahora podemos exponer selectivamente las funciones que queremos que el componente padre utilice. Esta solución mejora la claridad del código y proporciona una interfaz externa más eficiente y controlada.</p>
<h2>🧩 useLayoutEffect</h2>
<p><code>useLayoutEffect</code> en React es un gancho potente que te permite realizar operaciones síncronas después de que se hayan completado todas las mutaciones en el DOM, pero antes de que el navegador repinte la pantalla. Imagina el desafío al que te enfrentarías sin este gancho.</p>
<h3>🤔 Desafío Sin <code>useLayoutEffect</code>:</h3>
<p>Imagina un escenario en el que queremos medir las dimensiones de un elemento DOM y, basándonos en esas dimensiones, realizar ciertas acciones en nuestro componente funcional <code>ResizableBox</code>. Sin <code>useLayoutEffect</code>, podríamos tener problemas al realizar estas mediciones de manera asíncrona.</p>
<pre><code class="language-tsx">export const ResizableBox = () =&gt; {
  const boxRef = useRef&lt;HTMLDivElement&gt;(null)
  const [boxWidth, setBoxWidth] = useState(0)

  useEffect(() =&gt; {
    // Desafío: ¿Cómo medir las dimensiones de boxRef de manera síncrona?
    // Las mediciones pueden no estar disponibles de inmediato.
    // Esto puede causar problemas si dependemos de las dimensiones para realizar acciones.
  }, [])

  return &lt;div ref={boxRef}&gt;{/* Contenido de la caja redimensionable */}&lt;/div&gt;
}
</code></pre>
<p>En este escenario, realizar mediciones síncronas de las dimensiones de <code>boxRef</code> es desafiante con <code>useEffect</code> porque las mediciones pueden no estar disponibles de inmediato. Aquí es donde entra en juego <code>useLayoutEffect</code>.</p>
<h3>✅ Solución con <code>useLayoutEffect</code>:</h3>
<pre><code class="language-tsx">export const ResizableBox = () =&gt; {
  const boxRef = useRef&lt;HTMLDivElement&gt;(null)
  const [boxWidth, setBoxWidth] = useState(0)

  useLayoutEffect(() =&gt; {
    // Usa useLayoutEffect para medir las dimensiones de boxRef de manera síncrona
    if (boxRef.current) {
      setBoxWidth(boxRef.current.offsetWidth)
    }
  }, [])

  return &lt;div ref={boxRef}&gt;{/* Contenido de la caja redimensionable */}&lt;/div&gt;
}
</code></pre>
<p>Con <code>useLayoutEffect</code>, podemos realizar mediciones síncronas después de que el navegador haya completado todas las mutaciones en el DOM pero antes de repintar la pantalla. Esto asegura que las mediciones estén disponibles de inmediato y evita problemas potenciales relacionados con la asincronía.</p>
<h2>🐞 useDebugValue</h2>
<p><code>useDebugValue</code> en React es un gancho especializado diseñado para mejorar la experiencia de depuración al proporcionar información adicional sobre el estado de un componente. Su principal utilidad es facilitar la identificación de componentes en las herramientas de desarrollo del navegador.</p>
<p>La firma básica para <code>useDebugValue</code> es la siguiente:</p>
<pre><code class="language-tsx">useDebugValue(value)
</code></pre>
<p>Al depurar una aplicación de React, es común enfrentarse al desafío de identificar rápidamente el componente de interés dentro de las herramientas de desarrollo del navegador. Sin <code>useDebugValue</code>, la información sobre un componente puede ser limitada y no específica.</p>
<p><strong>Así es cómo se usa el gancho <code>useDebugValue</code>:</strong></p>
<h3>1. Agregar un Nombre Descriptivo:</h3>
<pre><code class="language-tsx">import { useDebugValue } from &#39;react&#39;

const useCustomHook = () =&gt; {
  const state =
    /* lógica del gancho */

    // Proporcionar un nombre descriptivo para facilitar la identificación en las herramientas de desarrollo
    useDebugValue(&#39;Custom Hook&#39;)

  return state
}
</code></pre>
<h3>2. Mostrar Contenido de un Array:</h3>
<pre><code class="language-tsx">import { useDebugValue } from &#39;react&#39;

const useArrayHook = (array: string[]) =&gt; {
  // lógica del gancho

  // Mostrar el contenido del array en las herramientas de desarrollo
  useDebugValue(array.join(&#39;, &#39;))

  return /* resultado */
}
</code></pre>
<h3>3. Utilizar Información del Estado:</h3>
<pre><code class="language-tsx">import { useState, useDebugValue } from &#39;react&#39;

const useCounter = () =&gt; {
  const [count, setCount] = useState(0)

  // Utilizar el valor del contador como información en las herramientas de desarrollo
  useDebugValue(`Contador: ${count}`)

  return [count, setCount]
}
</code></pre>
<p>Al usar <code>useDebugValue</code>, puedes personalizar la información que se muestra sobre tu gancho o componente en las herramientas de desarrollo, facilitando su identificación y depuración durante el desarrollo.</p>
<h2>🚀 Conclusión</h2>
<p>En esta exploración de React Hooks, desde los conceptos más básicos hasta los más avanzados, confío en que hayas descubierto el fascinante mundo que estos ganchos ofrecen para simplificar y potenciar tus desarrollos en React. ¡Feliz codificación! ✨</p>
<h3>🔗 Referencias</h3>
<ul>
<li><a href="https://react.dev/reference/react" target="_blank" rel="noopener noreferrer">https://react.dev/reference/react</a></li>
<li><a href="https://youtube.com/playlist?list=PLUofhDIg_38q4D0xNWp7FEHOTcZhjWJ29&si=38k-uyCK8hlf7DIk" target="_blank" rel="noopener noreferrer">https://www.youtube.com/@midulive</a></li>
</ul>
<h3>🌐 Recursos Adicionales</h3>
<ul>
<li><a href="https://blog.logrocket.com/react-hooks-cheat-sheet-solutions-common-problems/" target="_blank" rel="noopener noreferrer">Documentación de React Hooks: Una versión fácil de leer</a></li>
</ul>
</article>]]></description>
            <link>https://www.fraineralex.dev/es/blog/crafting-react-hooks</link>
            <guid isPermaLink="false">https://www.fraineralex.dev/es/blog/crafting-react-hooks</guid>
            <category><![CDATA[react]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[typescript]]></category>
            <dc:creator><![CDATA[Frainer Encarnación]]></dc:creator>
            <pubDate>Wed, 24 Jan 2024 00:00:00 GMT</pubDate>
            <enclosure url="https://www.fraineralex.dev/es/blog/images/blog/posts/cover/rss/crafting-react-hooks.png" length="0" type="png"/>
        </item>
        <item>
            <title><![CDATA[Dominando los Mensajes de Commit en Git]]></title>
            <description><![CDATA[
    <header>
      <figure>
        <img
          alt="undefined"
          loading="lazy"
          decoding="async"
          data-nimg="1"
          src="https://www.fraineralex.dev/images/blog/posts/cover/master-git-commit-message.webp"
          style="margin-top: 2rem; color: transparent; min-width: 80%; border-radius: .75rem; margin-left: auto; margin-right: auto; width: 100%; height: auto;"
        />
        <figcaption style=" text-align: right; font-size: 0.875rem; line-height: 1.25rem; padding-inline-end: 2rem; color: darkgray; padding-inline-end: 2rem; padding-top: 0.25rem;  font-style: italic;">
          Fuente: Roman Synkevych en Unsplash
        </figcaption>
      </figure>
      <p style=" margin-left: auto; margin-right: auto; text-transform: uppercase; text-align: center; padding-bottom: 2rem;"
        ><span style="margin-bottom: 0;">
          <time datetime="2024-01-20"
            >Jan 20, 2024</time
          >
          <span style="padding-left: 1rem; padding-right: 1rem;">•</span
          ><span style="margin-inline-end: 0;">8 min de lectura</span>
          <span style="padding-left: 1rem; padding-right: 1rem;">|</span>
          
                  <a href="https://www.fraineralex.dev/tags/git" style="color: black; text-decoration-line: underline; text-underline-offset: 4px;">
                    Git
                  </a>
                  <span style="padding-left: 10px; padding-right: 10px;"> • </span>
              
                  <a href="https://www.fraineralex.dev/tags/terminal" style="color: black; text-decoration-line: underline; text-underline-offset: 4px;">
                    Terminal
                  </a>
                  
              
          </span>
      </p>  
    </header>
  <article style="text-wrap: balance;"><p>En esta publicación, te mostraré cómo mejorar tus mensajes de commit en Git utilizando las mejores prácticas de la industria, con el objetivo de asegurar un historial de Git claro y fácilmente legible para tus proyectos.</p>
<h2>🧠 Comprendiendo los Commits en Git</h2>
<p>Un <code>commit</code> en Git captura un conjunto de cambios almacenados en la historia de un proyecto, representando una unidad lógica de trabajo. Los commits son fundamentales para el control de versiones, fomentando la colaboración efectiva entre desarrolladores, organizando registros de actualizaciones y facilitando el retorno a versiones anteriores.</p>
<h3>Estructura del Commit</h3>
<p><strong>Básico:</strong> <code>git commit -m &lt;mensaje&gt;</code></p>
<p><strong>Detallado:</strong> <code>git commit -m &lt;título&gt; -m &lt;descripción&gt;</code></p>
<pre><code class="language-bash">$ git add .
$ git commit -m &quot;Implement user sign-in feature&quot;
</code></pre>
<blockquote>
<p><strong>🚨 Importante:</strong> Este artículo asume que ya comprendes el flujo básico de trabajo con Git. Si no es así, te sugiero leer el <a href="https://guides.github.com/introduction/git-handbook/" target="_blank" rel="noopener noreferrer">Manual de Git</a>.</p>
</blockquote>
<h2>🌟 <strong>La Importancia de Mensajes de Commit Bien Elaborados</strong></h2>
<p>Los commits no son solo instantáneas de cambios de código; sirven como una hoja de ruta para la historia de tu proyecto. Un mensaje de commit mal escrito puede causar confusión, haciendo que sea difícil entender el contexto de los cambios. Exploraremos por qué la elaboración de mensajes de commit significativos es importante.</p>
<p><strong>Échale un vistazo a una mala implementación de los mensajes de commit</strong></p>
<p><img src="https://fraineralex.dev/images/blog/posts/content/git-commit.webp" alt="Ejemplo de mensajes de commit incorrectos"><br><em>Fuente: <a href="http://xkcd.com/" target="_blank" rel="noopener noreferrer">xkcd.com</a></em></p>
<p>Piensa en tus mensajes de commit como cartas para tu yo futuro o colaboradores. Al invertir tiempo en escribir mensajes informativos, creas un rastro de documentación que puede ahorrar horas de solución de problemas y mejorar el mantenimiento del proyecto, especialmente en proyectos a gran escala.</p>
<h2>🤔 <strong>Por qué Importa la Calidad del Mensaje de Commit</strong></h2>
<p>Te desafío a revisar viejos mensajes de commit en tus proyectos usando <code>git log</code>. Puedes encontrarte con mensajes poco claros que brindan poca información sobre los cambios realizados. Sin una documentación adecuada, podrías encontrarte pensando en el propósito de ciertas líneas de código.</p>
<p>Al adherirse a buenas prácticas de commit, aseguras el futuro de tu trabajo. Los mensajes claros se convierten en un activo valioso durante los esfuerzos colaborativos, fomentando una mejor comunicación dentro de los equipos de ingeniería.</p>
<h2>🎬 <strong>6 Reglas para Elaborar Mensajes de Commit Excepcionales</strong></h2>
<p>Hay 6 reglas simples que ayudarán a mantener consistencia, claridad y eficacia en tus mensajes de commit, mejorando la comunicación dentro del equipo de desarrollo y facilitando el seguimiento del historial de código. Estas reglas son:</p>
<h3>1. Usa el Modo Imperativo en la Línea del Asunto</h3>
<p>Al redactar un mensaje de commit, usa el modo imperativo en la línea del asunto para transmitir la acción realizada. Por ejemplo, en lugar de decir &quot;Añadida funcionalidad&quot;, usa &quot;Añadir funcionalidad&quot; o &quot;Corregir error&quot; en lugar de &quot;Corregido error&quot;. Esto proporciona una instrucción clara sobre el cambio de estado previsto.</p>
<p>Para crear commits reflexivos, considera lo siguiente:</p>
<ul>
<li>¿Por qué he realizado estos cambios?</li>
<li>¿Qué efecto han tenido mis cambios?</li>
<li>¿Por qué se necesitaba el cambio?</li>
<li>¿A qué se refieren los cambios?</li>
</ul>
<p>La respuesta podría ser algo así:</p>
<pre><code class="language-bash">- Añadir la nueva página de inicio de sesión
- Corregir un problema con el feed del blog
- Cambiar la familia de fuentes principal a Inter
- Eliminar un artículo al azar
</code></pre>
<h3>2. Evita Puntos y Puntos Suspensivos en los Mensajes de Commit</h3>
<p>Evita el uso de puntuación innecesaria en los mensajes de commit. La primera línea de un commit sirve como título y no debe terminar con un punto. Además, se deben evitar los puntos suspensivos para obtener instrucciones claras y concisas.</p>
<pre><code class="language-bash">git commit -m &quot;Add the new login page.&quot; # ❌
git commit -m &quot;Add the new login page...&quot; # ❌
git commit -m &quot;Add the new login page&quot; # ✅
</code></pre>
<h3>3. Mantén la Línea del Asunto por Debajo de 50 Caracteres</h3>
<p>Sé breve y directo al punto. Si tu mensaje de commit es demasiado extenso, considera dividirlo en varios commits. Asegúrate de que la línea del asunto refleje con precisión los cambios realizados.</p>
<pre><code class="language-bash">git commit -m &quot;Add the article &#39;How to install Bun on Windows&#39; and update the blog&#39;s description in the home page&quot; # ❌
git commit -m &quot;Add the article &#39;How to install Bun on Windows&#39;&quot; # ✅
git commit -m &quot;Update the blog&#39;s description in the home page&quot; # ✅
</code></pre>
<h3>4. Proporciona Contexto Necesario en el Cuerpo del Commit</h3>
<p>Cuando se requiere contexto adicional, utiliza el cuerpo del commit en lugar de saturar la línea del asunto. Esto se puede lograr usando <code>git commit</code> sin la bandera <code>-m</code>.</p>
<pre><code class="language-bash"># Ejemplo
git commit fix: prevent racing of requests

Introduce a request id and a reference to latest request. Dismiss
incoming responses other than from latest request.

Remove timeouts which were used to mitigate the racing issue but are
obsolete now.
</code></pre>
<p>Recuerda que la primera línea del mensaje del commit sirve como título, mientras que las líneas siguientes forman el cuerpo y deben adherirse a las reglas estándar de puntuación.</p>
<h3>5. Usa Prefijos para Commits Semánticos</h3>
<p>A medida que tu proyecto crece, mantener un historial legible se vuelve crucial. Para lograr esto, puedes agregar un prefijo para dar más significado a los commits que realizas. Esto se llama commits semánticos y se hace de la siguiente manera:</p>
<pre><code class="language-bash">&lt;tipo&gt;[ámbito opcional]: &lt;descripción&gt;

[cuerpo opcional]

[pie(s) opcional(es)]

# Ejemplo:
fix: resolve login authentication bug
^-^  ^------------------------------^
│     │
│     └--&gt; # Descripción de los cambios
│
└──------&gt; # Tipo de cambio

[cuerpo opcional]
- Improved authentication logic to handle edge cases.
- Updated relevant unit tests.

[pie(s) opcional(es)]
Reviewed-by: X
Refs: #123
</code></pre>
<p>En <code>monorepositorios</code> con múltiples paquetes, también puedes agregar información sobre el paquete afectado por el commit. Esto se conoce como ámbito y se vería así:</p>
<pre><code class="language-bash">feat(backend): add a new table for the likes
fix(web): update the main font family to Inter
</code></pre>
<p>El tipo de commit puede incluir lo siguiente:</p>
<ul>
<li><code>feat</code> – se introduce una nueva funcionalidad con los cambios</li>
<li><code>fix</code> – se ha corregido un error</li>
<li><code>chore</code> – cambios que no están relacionados con una corrección o funcionalidad y no modifican archivos de código fuente o de prueba (por ejemplo, actualizaciones de dependencias)</li>
<li><code>refactor</code> – código refactorizado que no soluciona un error ni agrega una funcionalidad</li>
<li><code>docs</code> – actualizaciones de la documentación como el README u otros archivos markdown</li>
<li><code>style</code> – cambios que no afectan el significado del código, probablemente relacionados con el formato del código como espacios en blanco, falta de punto y coma, etc.</li>
<li><code>test</code> – inclusión de pruebas nuevas o corrección de pruebas anteriores</li>
<li><code>perf</code> – mejoras de rendimiento</li>
<li><code>ci</code> – relacionado con la integración continua</li>
<li><code>build</code> – cambios que afectan al sistema de construcción o a dependencias externas</li>
<li><code>revert</code> – revierte un commit anterior</li>
</ul>
<p>La línea del asunto del tipo de commit debe estar en minúsculas con un límite de caracteres para fomentar descripciones concisas.</p>
<p>El cuerpo opcional del commit debe usarse para proporcionar más detalles que no pueden ajustarse a las limitaciones de caracteres de la descripción de la línea del asunto.</p>
<p>También es un buen lugar para utilizar <code>BREAKING CHANGE: &lt;descripción&gt;</code> para señalar la razón de un cambio incompatible dentro del commit.</p>
<h3>6. Considera Usar Utilidades de Commit</h3>
<p>Mejora tu flujo de trabajo de commit incorporando herramientas como <code>husky</code> para ejecutar scripts antes de acciones específicas de Git y <code>commitlint</code> para asegurar que los commits sigan una convención elegida. Estas utilidades contribuyen a un proceso de commit más eficiente y estandarizado.</p>
<pre><code class="language-bash">npm install husky -D
npx husky install
npm set-script prepare &quot;husky install&quot;
npm run prepare
npx husky add .husky/pre-push &quot;npm test&quot;
git add .husky/pre-push

git commit -m &quot;Keep calm and commit&quot;
git push -u origin master *# tests run before the push
</code></pre>
<p>Con <code>commitlint</code>, puedes hacer cumplir commits semánticos y legibles que sigan convenciones predefinidas.</p>
<pre><code class="language-bash"># 1. Instalar commitlint cli y configuración convencional

# Para Mac y Linux:
npm install --save-dev @commitlint/{config-conventional,cli}
# Para Windows:
npm install --save-dev @commitlint/config-conventional @commitlint/cli

# 2. Agregar gancho para verificar el mensaje de commit
npx husky add .husky/commit-msg &#39;npx --no-install commitlint --edit &quot;$1&quot;&#39;
</code></pre>
<p>Estas herramientas, combinadas con prácticas como <code>conventional-changelog</code> y <code>commitizen</code>, contribuyen a un flujo de trabajo de commit eficiente y estandarizado, mejorando en última instancia la mantenibilidad y la colaboración del proyecto.</p>
<p>Al incorporar estas reglas y herramientas en tu flujo de trabajo de Git, contribuirás a un historial de proyecto más legible y organizado, facilitando que tú y tus colaboradores naveguen y comprendan los cambios realizados a lo largo del tiempo. ¡Felices commits! 🚀</p>
<h2>🌐 Recursos Adicionales</h2>
<ul>
<li><a href="https://www.conventionalcommits.org" target="_blank" rel="noopener noreferrer">Commits Convencionales</a></li>
<li><a href="https://semver.org" target="_blank" rel="noopener noreferrer">Versionado Semántico</a></li>
<li><a href="https://www.freecodecamp.org/news/how-to-write-better-git-commit-messages" target="_blank" rel="noopener noreferrer">freeCodeCamp.org</a></li>
</ul>
</article>]]></description>
            <link>https://www.fraineralex.dev/es/blog/mastering-git-commit-messages</link>
            <guid isPermaLink="false">https://www.fraineralex.dev/es/blog/mastering-git-commit-messages</guid>
            <category><![CDATA[git]]></category>
            <category><![CDATA[terminal]]></category>
            <dc:creator><![CDATA[Frainer Encarnación]]></dc:creator>
            <pubDate>Sat, 20 Jan 2024 00:00:00 GMT</pubDate>
            <enclosure url="https://www.fraineralex.dev/es/blog/images/blog/posts/cover/rss/master-git-commit-message.png" length="0" type="png"/>
        </item>
        <item>
            <title><![CDATA[Cómo instalar múltiples versiones de Node.js]]></title>
            <description><![CDATA[
    <header>
      <figure>
        <img
          alt="undefined"
          loading="lazy"
          decoding="async"
          data-nimg="1"
          src="https://www.fraineralex.dev/images/blog/posts/cover/how-to-install-nvm.webp"
          style="margin-top: 2rem; color: transparent; min-width: 80%; border-radius: .75rem; margin-left: auto; margin-right: auto; width: 100%; height: auto;"
        />
        <figcaption style=" text-align: right; font-size: 0.875rem; line-height: 1.25rem; padding-inline-end: 2rem; color: darkgray; padding-inline-end: 2rem; padding-top: 0.25rem;  font-style: italic;">
          Fuente: externlabs.com
        </figcaption>
      </figure>
      <p style=" margin-left: auto; margin-right: auto; text-transform: uppercase; text-align: center; padding-bottom: 2rem;"
        ><span style="margin-bottom: 0;">
          <time datetime="2024-01-18"
            >Jan 18, 2024</time
          >
          <span style="padding-left: 1rem; padding-right: 1rem;">•</span
          ><span style="margin-inline-end: 0;">6 min de lectura</span>
          <span style="padding-left: 1rem; padding-right: 1rem;">|</span>
          
                  <a href="https://www.fraineralex.dev/tags/javascript" style="color: black; text-decoration-line: underline; text-underline-offset: 4px;">
                    JavaScript
                  </a>
                  <span style="padding-left: 10px; padding-right: 10px;"> • </span>
              
                  <a href="https://www.fraineralex.dev/tags/nodejs" style="color: black; text-decoration-line: underline; text-underline-offset: 4px;">
                    Node.js
                  </a>
                  <span style="padding-left: 10px; padding-right: 10px;"> • </span>
              
                  <a href="https://www.fraineralex.dev/tags/terminal" style="color: black; text-decoration-line: underline; text-underline-offset: 4px;">
                    Terminal
                  </a>
                  
              
          </span>
      </p>  
    </header>
  <article style="text-wrap: balance;"><p>En este artículo, explicaré cómo instalar y gestionar múltiples versiones de Node.js con Node Version Manager (NVM) en Windows, Linux y Mac, y cambiar fácilmente entre ellas con un solo comando.</p>
<h2>🤔 ¿Qué es NVM?</h2>
<p>Node Version Manager (NVM), como su nombre indica, es una herramienta para gestionar versiones de Node en tu dispositivo.</p>
<p>Diferentes proyectos en tu dispositivo pueden estar utilizando diferentes versiones de Node. Usar solo una versión (la instalada por <code>npm</code>) para estos proyectos puede no proporcionarte resultados de ejecución precisos.</p>
<p>Por ejemplo, si utilizas una versión de Node como <strong>18.0.0</strong> para un proyecto que usa <strong>20.0.0</strong>, puedes obtener algunos errores. Y si actualizas la versión de Node a <strong>20.0.0</strong> con npm y la usas para un proyecto que usa <strong>18.0.0</strong>, es posible que no obtengas la experiencia esperada.</p>
<p>De hecho, lo más probable es que recibas una advertencia que dice:</p>
<pre><code class="language-bash">This project requires Node version X
</code></pre>
<p>En lugar de utilizar npm para instalar y desinstalar versiones de Node para tus diferentes proyectos, puedes usar <strong>nvm</strong>, que te ayuda a gestionar eficazmente las versiones de Node para cada proyecto.</p>
<p><a href="https://github.com/nvm-sh/nvm" target="_blank" rel="noopener noreferrer">NVM</a> te permite instalar diferentes versiones de Node y cambiar entre estas versiones dependiendo del proyecto en el que estés trabajando a través de la línea de comandos.</p>
<blockquote>
<p><strong>🚨 Importante:</strong> Esta guía asume que no tienes una instalación previa de <code>Node.js</code>. Si la tienes, podría causar problemas y conflictos. Antes de continuar, asegúrate de haber desinstalado cualquier instalación previa de <code>Node.js</code> y <code>npm</code>.</p>
</blockquote>
<h2>🐧 ¿Cómo instalar NVM en Mac, Linux o Windows WSL?</h2>
<p><strong>Este sería el método recomendado si tu sistema operativo se basa en UNIX</strong> (como Mac, Linux o Windows WSL), ya que ofrece varias ventajas, como manejar diferentes versiones de Node simultáneamente.</p>
<h3>1. Ejecuta el instalador de nvm</h3>
<p>Para instalar y actualizar <em>nvm</em>, debes ejecutar el script de instalación. Puedes descargar y ejecutar el código desde la terminal usando el siguiente comando:</p>
<pre><code class="language-bash">curl -o- &lt;https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh&gt; | bash

# o

wget -qO- &lt;https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh&gt; | bash
</code></pre>
<p>Puedes usar <code>curl</code> o <code>bash</code> dependiendo del comando disponible en tu dispositivo.</p>
<p>Este script clonará el <a href="https://github.com/nvm-sh/nvm" target="_blank" rel="noopener noreferrer">repositorio de nvm</a> en el directorio <code>~/.nvm</code> e intentará agregar las líneas de ejecución dependiendo del idioma de tu terminal (<code>~/.bash_profile</code>, <code>~/.zshrc</code>, <code>~/.profile</code> o <code>~/.bashrc</code>).</p>
<h3>2. Actualiza la configuración de tu perfil</h3>
<p>En caso de que no haya sido posible por alguna razón, es posible que te pida agregar manualmente las líneas de código. <strong>Esto solo es necesario si no se pudo hacer automáticamente.</strong> Las líneas a agregar serían estas:</p>
<pre><code class="language-bash">export NVM_DIR=&quot;$([ -z &quot;${XDG_CONFIG_HOME-}&quot; ] &amp;&amp; printf %s &quot;${HOME}/.nvm&quot; || printf %s &quot;${XDG_CONFIG_HOME}/nvm&quot;)&quot;
[ -s &quot;$NVM_DIR/nvm.sh&quot; ] &amp;&amp; \\\\. &quot;$NVM_DIR/nvm.sh&quot;
</code></pre>
<p>Después de hacer esto, es posible que necesites reiniciar la terminal o abrir una nueva pestaña para usar el comando <code>nvm</code>.</p>
<p>Este comando anterior carga nvm para su uso.</p>
<h3>3. Recarga la configuración de la shell</h3>
<p>Con tu configuración de perfil actualizada, ahora recargarás la configuración de tu terminal para usar:</p>
<pre><code class="language-bash">source ~/.bashrc
</code></pre>
<p>Con este comando ejecutado, nvm está listo para que lo uses. Puedes confirmar que nvm está instalado correctamente ejecutando:</p>
<pre><code class="language-bash">nvm -v
</code></pre>
<p>Esto debería mostrar la versión de nvm instalada.</p>
<h2>🪟 ¿Cómo instalar NVM en Windows?</h2>
<p><code>nvm-windows</code>, aunque comparte el nombre con <code>nvm</code>, no es de los mismos creadores y, por lo tanto, tiene algunas diferencias. Y entre esas diferencias está, por supuesto, la forma de instalarlo. Para <code>nvm-windows</code>, sigue estos pasos:</p>
<ol>
<li>Ve a la <a href="https://github.com/coreybutler/nvm-windows/releases" target="_blank" rel="noopener noreferrer">sección de lanzamientos de nvm-windows</a>.</li>
<li>Descarga y ejecuta el archivo <code>nvm-setup.exe</code> de la última versión disponible.</li>
<li>Ejecuta el archivo <code>nvm-setup.exe</code>.</li>
<li>Sigue los pasos de instalación guiada y elige la configuración deseada.</li>
<li>Una vez completado, abre una terminal de <code>PowerShell</code> y ejecuta:</li>
</ol>
<p>Cuando hayas terminado, puedes confirmar que nvm se ha instalado ejecutando:</p>
<pre><code class="language-bash">nvm -v
</code></pre>
<p>Si nvm se instaló correctamente, este comando te mostrará la versión de nvm instalada.</p>
<p>Y con esto, estamos listos para trabajar con <code>nvm-windows</code>. Si encuentras algún problema, puedes <strong>consultar la <a href="https://github.com/coreybutler/nvm-windows/wiki/Common-Issues" target="_blank" rel="noopener noreferrer">sección de Problemas Comunes del repositorio</a> para ver si tu problema tiene solución.</strong></p>
<h2>🕹️ ¿Cómo funciona NVM?</h2>
<p>Con nvm instalado, ahora puedes instalar, desinstalar y cambiar entre diferentes versiones de Node en tu dispositivo Windows, Linux o Mac.</p>
<p>Puedes instalar versiones de Node de esta manera:</p>
<pre><code class="language-bash">nvm install --lts
</code></pre>
<p>Esto instalará la <strong>versión de soporte a largo plazo de Node</strong>. Actualmente, es la versión 20, pero en el futuro será la 22 y así sucesivamente.</p>
<blockquote>
<p>LTS o soporte a largo plazo significa que es una versión que recibirá soporte durante un período más largo que otras versiones. Generalmente, es hasta 30 meses de</p>
</blockquote>
<p> soporte para errores críticos. Si vas a usar Node en producción, es mejor usar solo versiones LTS.</p>
<p>Si prefieres, también puedes instalar una versión específica indicando el número de versión principal o directamente la versión exacta:</p>
<pre><code class="language-bash">nvm install 21 # esto instalará la última versión 21 de Node.js
nvm install 12.6.0 # esto instalará exactamente la versión 21.6.0
</code></pre>
<h2>🚀 Uso de una Versión de Node.js Con NVM</h2>
<p>A veces, puedes tener más de una versión instalada de Node.js en tu usuario. Para ver todas las versiones de Node.js que tienes instaladas, puedes usar el comando <code>nvm ls</code>. Para ver las versiones disponibles para la instalación, debes usar <code>nvm ls-remote</code>.</p>
<p>Una vez que tienes más de una en tu sistema, querrás cambiar entre esas versiones. Para hacerlo, ejecuta <code>nvm use &lt;versión-que-quieres-usar&gt;</code>, por ejemplo:</p>
<pre><code class="language-bash">nvm use 20
nvm use 21
nvm use --lts
</code></pre>
<p>Si deseas que una de estas sea tu versión predeterminada, para que se use la próxima vez que abras una pestaña, debes crear un alias para esa versión llamado <code>default</code>.</p>
<p>Por ejemplo, supongamos que tienes instaladas las versiones 16 y 18, y ahora instalas la versión 20, y quieres que esta sea la versión predeterminada a partir de ahora. Deberías hacer lo siguiente:</p>
<pre><code class="language-bash">nvm install 20
nvm alias default 20
</code></pre>
<blockquote>
<p>La primera vez que ejecutas <code>nvm install</code>, esa versión se convierte en la predeterminada.</p>
</blockquote>
<h2>⚠️ Problemas Posibles</h2>
<ul>
<li>En Mac, necesitarás tener instaladas las herramientas de desarrollo de Xcode. Cuando ejecutas el script de instalación de <code>nvm</code> por primera vez, puede aparecer una ventana pidiéndote que hagas esto. Una vez instalado, intenta instalar <code>nvm</code> nuevamente con el mismo script que antes.</li>
<li>Recuerda, como mencioné, después de la instalación, es posible que necesites reiniciar la terminal o crear una nueva pestaña antes de poder ejecutar el comando <code>nvm</code>.</li>
<li>Si tienes problemas adicionales, puedes consultar la <a href="https://github.com/nvm-sh/nvm#troubleshooting-on-linux" target="_blank" rel="noopener noreferrer">sección de Resolución de Problemas en Linux en el repositorio de nvm</a>.</li>
</ul>
</article>]]></description>
            <link>https://www.fraineralex.dev/es/blog/how-to-install-multiple-versions-nodejs-nvm</link>
            <guid isPermaLink="false">https://www.fraineralex.dev/es/blog/how-to-install-multiple-versions-nodejs-nvm</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[terminal]]></category>
            <dc:creator><![CDATA[Frainer Encarnación]]></dc:creator>
            <pubDate>Thu, 18 Jan 2024 00:00:00 GMT</pubDate>
            <enclosure url="https://www.fraineralex.dev/es/blog/images/blog/posts/cover/rss/how-to-install-nvm.png" length="0" type="png"/>
        </item>
    </channel>
</rss>