<?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" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Cursos de programación freeCodeCamp en Español: Python, JavaScript, Git y más ]]>
        </title>
        <description>
            <![CDATA[ Descubre miles de cursos de programación escritos por expertos. Aprende Desarrollo Web, Ciencia de Datos, DevOps, Seguridad y obtén asesoramiento profesional para desarrolladores. ]]>
        </description>
        <link>https://www.freecodecamp.org/espanol/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Cursos de programación freeCodeCamp en Español: Python, JavaScript, Git y más ]]>
            </title>
            <link>https://www.freecodecamp.org/espanol/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Fri, 10 Apr 2026 13:45:12 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/espanol/news/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Aprende SQL - Curso desde cero en español ]]>
                </title>
                <description>
                    <![CDATA[ SQL (Structured Query Language) es el lenguaje estándar para gestionar y manipular datos almacenados en bases de datos relacionales. Es la herramienta esencial que te permite comunicarte con las bases de datos que impulsan las aplicaciones modernas. Acabamos de publicar un curso en el canal de YouTube de freeCodeCamp en ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/aprende-sql-curso-desde-cero-en-espanol/</link>
                <guid isPermaLink="false">69b88053633d5f04fc668404</guid>
                
                    <category>
                        <![CDATA[ SQL ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Estefania Cassingena Navone ]]>
                </dc:creator>
                <pubDate>Thu, 26 Mar 2026 04:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2026/03/Thumbnail-A.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>SQL (Structured Query Language) es el lenguaje estándar para gestionar y manipular datos almacenados en bases de datos relacionales. Es la herramienta esencial que te permite comunicarte con las bases de datos que impulsan las aplicaciones modernas.</p><p>Acabamos de publicar un curso en el canal de YouTube de freeCodeCamp en español que te guiará paso a paso a través de los fundamentos de SQL. Aprenderás los conceptos básicos que necesitas para comenzar y crearás dos proyectos.</p><p>El curso fue creado por Sergie Code. Sergie es ingeniero de software y creador de contenido a quien le encanta enseñar y compartir sus conocimientos. Su trabajo se centra en JavaScript, incluyendo React.js, Angular.js y otros frameworks.</p><p>Antes de adentrarnos en el contenido del curso, veamos qué es SQL y por qué deberías aprenderlo.</p><p>Los datos están en todas partes. Las plataformas y organizaciones modernas dependen de las bases de datos todos los días. Cada vez que inicias sesión en un sitio web, estás utilizando el poder de las bases de datos. Al dominar SQL, podrás extraer información valiosa a partir de los datos almacenados, una habilidad que es muy valorada en la ingeniería de software, la ciencia de datos y en los negocios.</p><p>SQL es un lenguaje especializado diseñado para gestionar datos almacenados en bases de datos relacionales. Con SQL, puedes crear bases de datos, definir su estructura y realizar operaciones clave como crear, insertar, actualizar y eliminar registros. Una de sus características principales es que te permite acceder a los datos a través de consultas (queries) para encontrar de forma eficiente los datos exactos que tú o tu aplicación necesitan.</p><h2 id="curso-de-sql-en-espa-ol"><strong>Curso de SQL en español</strong></h2><p>Genial. Ahora que ya sabes más sobre SQL, veamos lo que aprenderás durante el curso:</p><ul><li>Introducción a SQL y bases de datos.</li><li>SQL vs. MySQL, PostgreSQL y SQL Server.</li><li>Jerarquía de bases de datos relacionales.</li><li>DBngin y TablePlus.</li><li>Lenguaje de Definición de Datos (DDL): CREATE, DROP, ALTER y DESCRIBE.</li><li>Trabajo con tablas, atributos y restricciones.</li><li>Claves foráneas y relaciones entre tablas.</li><li>Lenguaje de Manipulación de Datos (DML): INSERT, UPDATE, DELETE y TRUNCATE.</li><li>Lenguaje de Consulta de Datos (DQL): SELECT, WHERE y ORDER BY.</li><li>Funciones de agregación (COUNT, SUM, AVG).</li><li>INNER JOIN, LEFT JOIN, RIGHT JOIN, y Joins de 3 tablas.</li><li>Subconsultas, GROUP BY y HAVING.</li><li>Gestión de SQL con Docker y variables de entorno.</li><li>Buenas prácticas y ejemplos prácticos.</li></ul><p>Al final del curso, conocerás los fundamentos de SQL y podrás comenzar a crear y gestionar tus propias bases de datos relacionales.</p><p>Te invitamos a ver el curso en el canal de YouTube de freeCodeCamp.org en español:</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/6JBsoPOwPew?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="Aprende SQL - Curso desde cero con MySQL, PostgreSQL, Backend y Docker" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><p>✍️ Curso creado por Sergie Code.</p><ul><li>Youtube: <a href="https://www.youtube.com/@SergieCode">@SergieCode</a></li><li>Instagram: <a href="https://www.instagram.com/sergiecode">sergiecode</a></li><li>LinkedIn: <a href="https://www.linkedin.com/in/sergiecode/">Sergie Code</a></li><li>GitHub: <a href="https://github.com/sergiecode">sergiecode</a></li><li>Facebook: <a href="https://www.facebook.com/sergiecodeok">sergiecodeok</a></li><li>TikTok: <a href="https://www.tiktok.com/@sergiecode">@sergiecode</a></li><li>Twitter: <a href="https://twitter.com/sergiecode">@sergiecode</a></li><li>Threads: <a href="https://www.threads.net/@sergiecode">@sergiecode</a></li></ul> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo construir una aplicación CRUD usando React y Convex ]]>
                </title>
                <description>
                    <![CDATA[ Las operaciones CRUD son la base de cada aplicación, por lo que es esencial volverse competente en esto cuando se aprende nuevas tecnologías. En este tutorial, aprenderás cómo construir una aplicación CRUD usando React y Convex. Cubriremos estas operaciones al construir un proyecto que se llama Book Collections. En este ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-construir-una-aplicacion-crud-usando-react-y-convex/</link>
                <guid isPermaLink="false">68102fd4c8db8f04fae91582</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ convex ]]>
                    </category>
                
                    <category>
                        <![CDATA[ crud ]]>
                    </category>
                
                    <category>
                        <![CDATA[ full stack ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Fri, 27 Jun 2025 01:18:13 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2025/04/Captura-desde-2025-04-28-22-49-09.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Las operaciones CRUD son la base de cada aplicación, por lo que es esencial volverse competente en esto cuando se aprende nuevas tecnologías.</p><p>En este tutorial, aprenderás cómo construir una aplicación CRUD usando React y Convex. Cubriremos estas operaciones al construir un proyecto que se llama Book Collections. En este proyecto, los usuarios serán capaz de agregar libros y actualizar sus estados una vez que completan un libro.</p><h2 id="tabla-de-contenido"><strong><strong>Tabl</strong>a<strong> </strong>de<strong> </strong>Contenido</strong></h2><!--kg-card-begin: markdown--><ul>
<li><a href="#que-es-convex">¿Qué es Convex?</a></li>
<li><a href="#como-configurar-proyecto">¿Cómo configurar tu proyecto?</a></li>
<li><a href="#como-crear-esquema">¿Cómo crear el esquema?</a></li>
<li><a href="#como-crear-ui">¿Cómo crear la UI?</a></li>
<li><a href="#como-crear-funciones-crud">¿Cómo crear las funciones CRUD?</a></li>
<li><a href="#estilos">Los estilos</a></li>
<li><a href="#resumen">Resumen</a></li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><h2 id="que-es-convex">¿Qué es Convex?</h2><!--kg-card-end: html--><p>Convex es la Plataforma BaaS que simplifica el desarrollo backend. Convex viene con una base de datos de tiempo real, y no tienes que preocuparte sobre escribir lógica de parte del servidor de forma separada porque provee métodos para solicitar y mutar la base de datos.</p><h3 id="pre-requisitos"><strong><strong>Pre</strong>-requisitos</strong></h3><p>Para seguir este tutorial, debes conocer los fundamentos de React. Estaré usando TypeScript en este proyecto, pero es opcional, así que puedes seguirme con JavaScript.</p><!--kg-card-begin: html--><h2 id="como-configurar-proyecto">Cómo configurar tu proyecto</h2><!--kg-card-end: html--><p>Crea una carpeta separa para el proyecto y llámalo como gustes – yo lo llamaré <strong>Books</strong>. Configuraremos Convex y React en esa carpeta.</p><p>Puedes crear una aplicación de React usando este comando:</p><pre><code class="language-bash">npm create vite@latest my-app -- --template react-ts
</code></pre><p>Si quieres trabajar con JavaScript, entonces quita el `ts` al final. Sería:</p><pre><code class="language-bash">npm create vite@latest my-app -- --template react
</code></pre><h3 id="c-mo-configurar-convex"><strong>Cómo configurar<strong> Convex</strong></strong></h3><p>Tenemos que instalar Convex en la misma carpeta. Puedes hacer eso usando este comando:</p><pre><code class="language-bash">npm install convex
</code></pre><p>Luego, ejecuta <code>npx convex dev</code>. Si lo estás haciendo por primera vez, te debería pedirte la autenticación. De otra forma, debería preguntarte por el nombre del proyecto.</p><p>Puedes visitar el <a href="https://www.convex.dev/">panel de Convex</a> para ver el proyecto que has creado.</p><p>Ahora que hemos configurado Convex y React, necesitamos conectar el backend de Convex a la aplicación de React.</p><p>En el <strong><code>src/main.tsx</code>, </strong>envuelve tu componente <code>App</code> con <code>ConvexReactClient</code>:</p><pre><code class="language-tsx">import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import { ConvexProvider, ConvexReactClient } from "convex/react";
import "./index.css"

const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string);

createRoot(document.getElementById("root")!).render(
  &lt;ConvexProvider client={convex}&gt;
    &lt;App /&gt;
  &lt;/ConvexProvider&gt;
);
</code></pre><p>Cuando configuras Convex, se crea un archivo <code>.env.local</code>. Puedes ver el URL de tu backend en ese archivo.</p><p>En la línea de abajo, instanciamos el Cliente de Convex de React con el URL.</p><pre><code class="language-typescript">const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string);
</code></pre><!--kg-card-begin: html--><h2 id="como-crear-esquema">Cómo crear el Esquema</h2><!--kg-card-end: html--><p>En tu carpeta principal del proyecto, deberías de ver la carpeta <strong>convex</strong>. Manejaremos las solicitudes de la base de datos y las mutaciones aquí.</p><p>Crea un archivo <strong>schema.ts</strong> en la carpeta <strong>convex:</strong></p><pre><code class="language-typescript">import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";

export default defineSchema({
  books: defineTable({
    title: v.string(),
    author: v.string(),
    isCompleted: v.boolean(),
  }),
});
</code></pre><p>Puedes definir un Esquema para tu documento con <code>defineSchema</code> y crea un tabla con <code>defineTable</code>. Convex provee estas funciones para definir un esquema y crear una tabla.</p><p><code>v</code> es el validador de tipo, se usa para proveer tipos para cada dato que agregamos a la tabla.</p><p>Para este proyecto, ya que es una aplicación de colección de libros, la estructura tendrá <code>title</code>, <code>author</code>, y <code>isCompleted</code>. Puedes agregar mas campos.</p><p>Ahora que tienes definido tu esquema, configuremos la UI básica en React.</p><!--kg-card-begin: html--><h2 id="como-crear-ui">Cómo crear la UI</h2><!--kg-card-end: html--><p>En la carpeta <strong>src</strong>, crea una carpeta llamada <strong>component</strong> y un archivo <strong>Home.tsx</strong>. Aquí, puedes definir la UI.</p><pre><code class="language-tsx">import { useState } from "react";
import "../styles/home.css";

const Home = () =&gt; {
  const [title, setTitle] = useState("");
  const [author, setAuthor] = useState("");
  return (
    &lt;div className="main-container"&gt;
      &lt;h1&gt;Book Collections&lt;/h1&gt;
      &lt;form onSubmit={handleSubmit}&gt;
        &lt;input
          type="text"
          name="title"
          value={title}
          onChange={(e) =&gt; setTitle(e.target.value)}
          placeholder="book title"
        /&gt;
        &lt;br /&gt;
        &lt;input
          type="text"
          name="author"
          value={author}
          onChange={(e) =&gt; setAuthor(e.target.value)}
          placeholder="book author"
        /&gt;
        &lt;br /&gt;
        &lt;input type="submit" /&gt;
      &lt;/form&gt;
      {books ? &lt;Books books={books} /&gt; : "Loading..."}
    &lt;/div&gt;
  );
};

export default Home;
</code></pre><p>Puedes crear tu componente como gustes. Agregué dos campos input <code>title</code>, <code>author</code>, y un botón <code>submit</code>. Esta es la estructura básica. Ahora podemos crear los métodos CRUD en el backend.</p><!--kg-card-begin: html--><h2 id="como-crear-funciones-crud">Cómo crear las funciones CRUD</h2><!--kg-card-end: html--><p>En la carpeta <strong>convex</strong>, puedes crear un archivo <strong>queries.ts</strong> separado para las funciones CRUD.</p><h3 id="la-funci-n-create"><strong>La<strong> </strong>Función Create</strong></h3><p>Ie <strong><strong>convex/queries.ts</strong></strong>:</p><p>Puedes definir una función <code>createBooks</code>. Puedes usar la función <code>mutation</code> de Convex para crear, actualizar, y eliminar datos. Leer los datos será a través de <code>query</code>.</p><p>La función <code>mutation</code> espera estos argumentos:</p><ul><li><code>agrs</code>: los datos que necesitamos almacenar en la base de datos.</li><li><code>handler</code>: maneja la lógica para almacenar los datos en la base de datos. El <code>handler</code> es una función asíncrona, y tiene dos argumentos: <code>ctx</code> y <code>args</code>. Aquí, <code>ctx</code> es el objeto contexto que usaremos para manejar las operaciones de la base de datos.</li></ul><p>Usarás el método <code>insert</code> para ingresar nuevos datos. El primer parámetro en el <code>insert</code> es el nombre de la tabla y el segundo es para los datos que necesitan ser insertados.</p><p>Como último, puedes regresar los datos desde la base de datos.</p><p>Aquí estás el código:</p><pre><code class="language-typescript">import { mutation} from "./_generated/server";
import { v } from "convex/values";

export const createBooks = mutation({
  args: { title: v.string(), author: v.string() },
  handler: async (ctx, args) =&gt; {
    const newBookId = await ctx.db.insert("books", {
      title: args.title,
      author: args.author,
      isCompleted: false,
    });
    return newBookId;
  },
});
</code></pre><h3 id="la-funci-n-read"><strong>La Función <strong>Read</strong></strong></h3><p>En <strong><strong>convex/queries.ts</strong></strong>:</p><pre><code class="language-typescript">import { query } from "./_generated/server";
import { v } from "convex/values";

//read
export const getBooks = query({
  args: {},
  handler: async (ctx) =&gt; {
    return await ctx.db.query("books").collect();
  },
});
</code></pre><p>En esta operación read, usamos la función incorporada <code>query</code> de Convex. Aquí, <code>args</code> estará vacía ya que no obtenemos ningún datos del usuario. De forma similar, la función <code>handler</code> es asíncrona y usa el objeto <code>ctx</code> para solicitar de la base de datos y regresar los datos.</p><h3 id="la-funci-n-update"><strong>La Función <strong>Update</strong></strong></h3><p>En <strong><strong>convex/queries.ts</strong></strong>:</p><p>Crea una función <code>updateStatus</code>. Vamos a actualizar solamente el estado <code>isCompleted</code>.</p><p>Aquí, necesitas obtener el ID del documento y el estado del usuario. En el <code>args</code>, definiremos el <code>id</code> y el <code>isCompleted</code>, los cuales vendrán del usuario.</p><p>En el <code>handler</code>, usaremos el método <code>patch</code> para actualizar los datos. El método <code>patch</code> espera dos argumentos: el primer argumento es el <code>id</code> del documento y el segundo es para los datos actualizados.</p><pre><code class="language-typescript">import { mutation } from "./_generated/server";
import { v } from "convex/values";

//update
export const updateStatus = mutation({
  args: { id: v.id("books"), isCompleted: v.boolean() },
  handler: async (ctx, args) =&gt; {
    const { id } = args;
    await ctx.db.patch(id, { isCompleted: args.isCompleted });
    return "updated"
  },
});
</code></pre><h3 id="funci-n-delete"><strong>Función <strong>Delete</strong></strong></h3><p>En <strong><strong>convex/queries.ts</strong></strong>:</p><p>Crea una función <code>deleteBooks</code> y usa la función <code>mutation</code>. Necesitaremos el ID del documento para que se elimine. En el <code>args</code>, define un ID. En el <code>handler</code>, usa el método <code>delete</code> del objeto <code>ctx</code>, y pasa el ID. Esto eliminará el documento.</p><pre><code class="language-typescript">import { mutation } from "./_generated/server";
import { v } from "convex/values";

//delete
export const deleteBooks = mutation({
  args: { id: v.id("books") },
  handler: async (ctx, args) =&gt; {
    await ctx.db.delete(args.id);
    return "deleted";
  },
});
</code></pre><p>A partir de ahora, has completado las funciones CRUD en el backend. Ahora necesitamos hacerlo funcionar en el UI. Volvamos a React.</p><h3 id="actualizar-el-ui"><strong>Actualizar<strong> </strong>el<strong> UI</strong></strong></h3><p>Ya has creado un UI básico en la aplicación de React, con algunos campos input. Vamos a actualizarlo.</p><p>En <strong><strong>src/component/Home.tsx</strong></strong>:</p><pre><code class="language-tsx">import { useQuery, useMutation } from "convex/react";
import { api } from "../../convex/_generated/api";
import { Books } from "./Books";
import { useState } from "react";
import "../styles/home.css";

const Home = () =&gt; {
  const [title, setTitle] = useState("");
  const [author, setAuthor] = useState("");
  const books = useQuery(api.queries.getBooks);
  const createBooks = useMutation(api.queries.createBooks);

  const handleSubmit = (e: React.FormEvent&lt;HTMLFormElement&gt;): void =&gt; {
    e.preventDefault();
    createBooks({ title, author })
      .then(() =&gt; {
        console.log("created");
        setTitle("");
        setAuthor("");
      })
      .catch((err) =&gt; console.log(err));
  };
  return (
    &lt;div className="main-container"&gt;
      &lt;h1&gt;Book Collections&lt;/h1&gt;
      &lt;form onSubmit={handleSubmit}&gt;
        &lt;input
          type="text"
          name="title"
          value={title}
          onChange={(e) =&gt; setTitle(e.target.value)}
          placeholder="book title"
        /&gt;
        &lt;br /&gt;
        &lt;input
          type="text"
          name="author"
          value={author}
          onChange={(e) =&gt; setAuthor(e.target.value)}
          placeholder="book author"
        /&gt;
        &lt;br /&gt;
        &lt;input type="submit" /&gt;
      &lt;/form&gt;
      {books ? &lt;Books books={books} /&gt; : "Loading..."}
    &lt;/div&gt;
  );
};

export default Home;
</code></pre><p>Ahora podemos usar las funciones API del backend al usar <code>api</code> de Convex. Como puedes ver, llamamos a dos funciones de la API: puedes usar <code>useQuery</code> si vas a obtener datos y <code>useMutation</code> si quieres cambiar los datos. Ahora en este archivo, que estamos haciendo, dos operaciones que son create y read.</p><p>Obtuvimos todos los datos al usar este método:</p><pre><code class="language-typescript"> const books = useQuery(api.queries.getBooks);
</code></pre><p>El arreglo de objetos serán almacenados en la variable books.</p><p>Obtuvimos la función create del backend con esta línea de código:</p><pre><code class="language-typescript">const createBooks = useMutation(api.queries.createBooks);
</code></pre><h3 id="c-mo-usar-la-funci-n-create-en-el-ui"><strong>Cómo usar la función<strong> </strong>create<strong> </strong>en el<strong> UI</strong></strong></h3><p>Usemos la función create en el UI.</p><p>Ya que los campos input están en la etiqueta <code>form</code>, usaremos el atributo <code>onSubmit</code> para manejar el envío del formulario.</p><pre><code class="language-typescript">// En el Home.tsx

const handleSubmit = (e: React.FormEvent&lt;HTMLFormElement&gt;): void =&gt; {
    e.preventDefault();
    createBooks({ title, author })
      .then(() =&gt; {
        console.log("created");
        setTitle("");
        setAuthor("");
      })
      .catch((err) =&gt; console.log(err));
  };
</code></pre><p>Cuando haces clic en submit, dispara la función <code>handleSubmit</code>.</p><p>Usamos el <code>createBooks</code> para pasar el <code>title</code> y <code>author</code> del estado. La función del endpoint es async, por lo que podemos usar el <code>handleSubmit</code> como async o usar <code>.then</code>. Usé el método <code>.then</code> para manejar los datos asíncronos.</p><p>Puedes crear un componente separado para mostrar los datos solicitados de la base de datos. Los datos regresados están en el <strong>Home.tsx</strong>, así que pasaremos los datos al componente <strong>Book.jsx</strong> como props.</p><p>En <strong><strong>Books.tsx</strong></strong>:</p><pre><code class="language-tsx">import { useState } from "react";
import { book } from "../types/book.type";
import { useMutation } from "convex/react";
import { api } from "../../convex/_generated/api";
import { Id } from "../../convex/_generated/dataModel";
import "../styles/book.css";

export const Books = ({ books }: { books: book[] }) =&gt; {
  const [update, setUpdate] = useState(false);
  const [id, setId] = useState("");

  const deleteBooks = useMutation(api.queries.deleteBooks);
  const updateStatus = useMutation(api.queries.updateStatus);

  const handleClick = (id: string) =&gt; {
    setId(id);
    setUpdate(!update);
  };

  const handleDelete = (id: string) =&gt; {
    deleteBooks({ id: id as Id&lt;"books"&gt; })
      .then((mess) =&gt; console.log(mess))
      .catch((err) =&gt; console.log(err));
  };

  const handleUpdate = (e: React.FormEvent&lt;HTMLFormElement&gt;, id: string) =&gt; {
    e.preventDefault();
    const formdata = new FormData(e.currentTarget);
    const isCompleted: boolean =
      (formdata.get("completed") as string) === "true";
    updateStatus({ id: id as Id&lt;"books"&gt;, isCompleted })
      .then((mess) =&gt; console.log(mess))
      .catch((err) =&gt; console.log(err));
    setUpdate(false);
  };

  return (
    &lt;div&gt;
      {books.map((data: book, index: number) =&gt; {
        return (
          &lt;div
            key={data._id}
            className={`book-container ${data.isCompleted ? "completed" : "not-completed"}`}
          &gt;
            &lt;h3&gt;Book no: {index + 1}&lt;/h3&gt;
            &lt;p&gt;Book title: {data.title}&lt;/p&gt;
            &lt;p&gt;Book Author: {data.author}&lt;/p&gt;
            &lt;p&gt;
              Completed Status:{" "}
              {data.isCompleted ? "Completed" : "Not Completed"}
            &lt;/p&gt;
            &lt;button onClick={() =&gt; handleClick(data._id)}&gt;Update&lt;/button&gt;
            {id === data._id &amp;&amp; update &amp;&amp; (
              &lt;&gt;
                &lt;form onSubmit={(e) =&gt; handleUpdate(e, data._id)}&gt;
                  &lt;select name="completed"&gt;
                    &lt;option value="true"&gt;Completed&lt;/option&gt;
                    &lt;option value="false"&gt;Not Completed&lt;/option&gt;
                  &lt;/select&gt;
                  &lt;input type="submit" /&gt;
                &lt;/form&gt;
              &lt;/&gt;
            )}
            &lt;button onClick={() =&gt; handleDelete(data._id)}&gt;delete&lt;/button&gt;
          &lt;/div&gt;
        );
      })}
    &lt;/div&gt;
  );
};
</code></pre><p>En el componente <strong>Book.jsx, </strong>puedes mostrar los datos de la base de datos y manejar la funcionalidad para actualizar y eliminar los registros.</p><p>Veamos paso a paso cada una de estas características.</p><h3 id="c-mo-mostar-los-datos"><strong>Cómo mostar<strong> </strong>los<strong> Dat</strong>os</strong></h3><p>Puedes obtener los datos pasados como un prop en el componente <code>Home.tsx</code>. Si estás usando TypeScript, he definido un tipo para el objeto que es regresado de la solicitud. Puedes ignorar esto si estás usando JavaScript.</p><p>Crea <strong><strong><code>books.types.ts</code></strong></strong>:</p><pre><code class="language-typescript">export type book = {
    _id: string,
    title: string,
    author: string,
    isCompleted: boolean
}
</code></pre><p>Puedes usar la función <code>map</code> para mostrar los datos.</p><pre><code class="language-tsx">import { useState } from "react";
import { book } from "../types/book.type";
import { useMutation } from "convex/react";
import { api } from "../../convex/_generated/api";
import { Id } from "../../convex/_generated/dataModel";
import "../styles/book.css";

export const Books = ({ books }: { books: book[] }) =&gt; {
  const [update, setUpdate] = useState(false);

  return (
    &lt;div&gt;
      {books.map((data: book, index: number) =&gt; {
        return (
          &lt;div
            key={data._id}
            className={`book-container ${data.isCompleted ? "completed" : "not-completed"}`}
          &gt;
            &lt;h3&gt;Book no: {index + 1}&lt;/h3&gt;
            &lt;p&gt;Book title: {data.title}&lt;/p&gt;
            &lt;p&gt;Book Author: {data.author}&lt;/p&gt;
            &lt;p&gt;
              Completed Status:{" "}
              {data.isCompleted ? "Completed" : "Not Completed"}
            &lt;/p&gt;
            &lt;button onClick={() =&gt; handleClick(data._id)}&gt;Update&lt;/button&gt;
            {id === data._id &amp;&amp; update &amp;&amp; (
              &lt;&gt;
                &lt;form onSubmit={(e) =&gt; handleUpdate(e, data._id)}&gt;
                  &lt;select name="completed"&gt;
                    &lt;option value="true"&gt;Completed&lt;/option&gt;
                    &lt;option value="false"&gt;Not Completed&lt;/option&gt;
                  &lt;/select&gt;
                  &lt;input type="submit" /&gt;
                &lt;/form&gt;
              &lt;/&gt;
            )}
            &lt;button onClick={() =&gt; handleDelete(data._id)}&gt;delete&lt;/button&gt;
          &lt;/div&gt;
        );
      })}
    &lt;/div&gt;
  );
};
</code></pre><p>Esta es la estructura básica. Mostramos el título, autor, y estado, junto con un botón actualizar y eliminar.</p><p>Ahora, agreguemos las funcionalidades.</p><pre><code class="language-tsx">import { useState } from "react";
import { book } from "../types/book.type";
import { useMutation } from "convex/react";
import { api } from "../../convex/_generated/api";
import { Id } from "../../convex/_generated/dataModel";
import "../styles/book.css";

export const Books = ({ books }: { books: book[] }) =&gt; {
  const [update, setUpdate] = useState(false);
  const [id, setId] = useState("");

  const deleteBooks = useMutation(api.queries.deleteBooks);
  const updateStatus = useMutation(api.queries.updateStatus);

  const handleClick = (id: string) =&gt; {
    setId(id);
    setUpdate(!update);
  };

  const handleDelete = (id: string) =&gt; {
    deleteBooks({ id: id as Id&lt;"books"&gt; })
      .then((mess) =&gt; console.log(mess))
      .catch((err) =&gt; console.log(err));
  };

  const handleUpdate = (e: React.FormEvent&lt;HTMLFormElement&gt;, id: string) =&gt; {
    e.preventDefault();
    const formdata = new FormData(e.currentTarget);
    const isCompleted: boolean =
      (formdata.get("completed") as string) === "true";
    updateStatus({ id: id as Id&lt;"books"&gt;, isCompleted })
      .then((mess) =&gt; console.log(mess))
      .catch((err) =&gt; console.log(err));
    setUpdate(false);
  };

  return (
    &lt;div&gt;
      {books.map((data: book, index: number) =&gt; {
        return (
          &lt;div
            key={data._id}
            className={`book-container ${data.isCompleted ? "completed" : "not-completed"}`}
          &gt;
            &lt;h3&gt;Book no: {index + 1}&lt;/h3&gt;
            &lt;p&gt;Book title: {data.title}&lt;/p&gt;
            &lt;p&gt;Book Author: {data.author}&lt;/p&gt;
            &lt;p&gt;
              Completed Status:{" "}
              {data.isCompleted ? "Completed" : "Not Completed"}
            &lt;/p&gt;
            &lt;button onClick={() =&gt; handleClick(data._id)}&gt;Update&lt;/button&gt;
            {id === data._id &amp;&amp; update &amp;&amp; (
              &lt;&gt;
                &lt;form onSubmit={(e) =&gt; handleUpdate(e, data._id)}&gt;
                  &lt;select name="completed"&gt;
                    &lt;option value="true"&gt;Completed&lt;/option&gt;
                    &lt;option value="false"&gt;Not Completed&lt;/option&gt;
                  &lt;/select&gt;
                  &lt;input type="submit" /&gt;
                &lt;/form&gt;
              &lt;/&gt;
            )}
            &lt;button onClick={() =&gt; handleDelete(data._id)}&gt;delete&lt;/button&gt;
          &lt;/div&gt;
        );
      })}
    &lt;/div&gt;
  );
};
</code></pre><p>Este es todo el código del componente. Déjame explicarte qué hice.</p><p>Primero, necesitamos alternar la actualización, así que definimos la función <code>handleClick</code>, y le pasamos un ID del documento.</p><pre><code class="language-typescript">//handleClick
 const handleClick = (id: string) =&gt; {
    setId(id);
    setUpdate(!update);
  };
</code></pre><p>En el <code>handleClick</code> puedes actualizar el estado del ID y alternar el estado de la actualización de forma que alternará la actualización cuando se haga clic, y en otro clic, se cerrará.</p><p>Luego, tenemos <code>handleUpdate</code>. Necesitamos el ID del documento para actualizar los datos, así que pasamos el objeto evento así también como el ID del documento. Para obtener la entrada, podemos usar <code>FormData</code>.</p><pre><code class="language-typescript">const updateStatus = useMutation(api.queries.updateStatus);

const handleUpdate = (e: React.FormEvent&lt;HTMLFormElement&gt;, id: string) =&gt; {
    e.preventDefault();
    const formdata = new FormData(e.currentTarget);
    const isCompleted: boolean =
      (formdata.get("completed") as string) === "true";
    updateStatus({ id: id as Id&lt;"books"&gt;, isCompleted })
      .then((mess) =&gt; console.log(mess))
      .catch((err) =&gt; console.log(err));
    setUpdate(false);
  };
</code></pre><p>Necesitamos usar el <code>useMutation</code> para obtener la función <code>updateStatus</code>. Pasa el ID y el estado completado a la función, y maneja la parte asíncrona usando <code>.then</code>.</p><p>Para la función delete, el ID del documento es suficiente. Como el anterior, llama la función delete usando el <code>useMutation</code> y pásale el ID.</p><p>Luego pasa el ID del documento y maneja la promesa.</p><pre><code class="language-typescript">const deleteBooks = useMutation(api.queries.deleteBooks);

const handleDelete = (id: string) =&gt; {
    deleteBooks({ id: id as Id&lt;"books"&gt; })
      .then((mess) =&gt; console.log(mess))
      .catch((err) =&gt; console.log(err));
 };
</code></pre><!--kg-card-begin: html--><h2 id="estilos">Estilos</h2><!--kg-card-end: html--><p>Finalmente, lo que queda es agregar algo de estilo. Agregué algunos estilos básicos. Si el libro no ha sido completado, estará en rojo, y si el libro se ha completado, estará en verde.</p><p>Aquí la captura de pantalla:</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729428111374/1d1a69ef-5d35-4410-91f4-d8cf4817991d.png" class="kg-image" alt="final output" width="851" height="601" loading="lazy"></figure><p>¡¡Esto es todo chicos!!</p><p>Puedes visitar mi repositorio para ver todo el código: <a href="https://github.com/sanjayr-12/convex-crud">convex-curd</a></p><!--kg-card-begin: html--><h2 id="resumen">Resumen</h2><!--kg-card-end: html--><p>En este artículo, implementamos las operaciones CRUD (Crear, Leer, Actualizar y Eliminar) al construir una aplicación de colecciones de libros. Comenzamos configurando Convex y React, y escribir la lógica de CRUD.</p><p>Este tutorial cubrió tanto el frontend como el backend, demostrando cómo construir una aplicación serverless.</p><p>Puedes encontrar todo el código aquí: <a href="https://github.com/sanjayr-12/convex-crud">convex-curd</a></p><p>Si hay algún error o cualquier duda, contáctame en <a href="https://www.linkedin.com/in/sanjay-r-ab6064294/">LinkedIn</a>, <a href="https://www.instagram.com/_sanjayxr_12_/">Instagram</a>.</p><p>¡Gracias por leer!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Aprende React en tu Navegador: freeCodeCamp Full Stack Curriculum Mid-2025 Update ]]>
                </title>
                <description>
                    <![CDATA[ ¡Hola! La comunidad freeCodeCamp sigue trabajando muy duro en el resto de los cursos de nuestro plan de estudios de full stack [https://www.freecodecamp.org/learn/full-stack-developer/]. Solo han pasado unos meses, pero ya hay muchos estudiantes que van con toda su fuerza a través de estas nuevas lecciones y desafíos de programación. Estoy ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/aprende-react-en-tu-navegador-freecodecamp-full-stack-curriculum-mid-2025-update/</link>
                <guid isPermaLink="false">68557fb24e611704e4ae22e1</guid>
                
                    <category>
                        <![CDATA[ freeCodeCamp ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rafael D. Hernandez ]]>
                </dc:creator>
                <pubDate>Fri, 20 Jun 2025 15:48:45 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2025/06/fullstack.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/learn-react-in-your-browser-freecodecamp-full-stack-curriculum-mid-2025-update/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Learn React in your Browser – freeCodeCamp Full Stack Curriculum Mid-2025 Update</a>
      </p><p>¡Hola! La comunidad freeCodeCamp sigue trabajando muy duro en el resto de los cursos de nuestro plan de estudios de <a href="https://www.freecodecamp.org/learn/full-stack-developer/">full stack</a>.</p><p>Solo han pasado unos meses, pero ya hay muchos estudiantes que van con toda su fuerza a través de estas nuevas lecciones y desafíos de programación.</p><p>Estoy emocionada de compartir nuestra próxima ola de actualizaciones con todos ustedes.</p><h2 id="nuevo-plan-de-estudios">Nuevo plan de estudios</h2><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2025/06/6e973e4a-4b7e-4294-b056-95a5f6155e77.png" class="kg-image" alt="6e973e4a-4b7e-4294-b056-95a5f6155e77" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2025/06/6e973e4a-4b7e-4294-b056-95a5f6155e77.png 600w, https://www.freecodecamp.org/espanol/news/content/images/2025/06/6e973e4a-4b7e-4294-b056-95a5f6155e77.png 717w" width="717" height="766" loading="lazy"></figure><p><br>Acabamos de lanzar tres nuevas secciones del plan de estudios: React Hooks y Estado, Performance y Testing.</p><p>Este nuevo contenido incluye:</p><ul><li>50 videos explicativos</li><li>una docena de talleres y laboratorios</li><li>tres bloques de repaso nuevos</li><li>y mucho material para ayudarte a seguir avanzando mientras terminamos el resto del contenido Full Stack.</li></ul><p>Algunos proyectos que vas a construir:</p><ul><li>un juego completo de Tic Tac Toe</li><li>un selector de colores</li><li>y un formulario para aplicar como superhéroe 🦸‍♂️</li></ul><h2 id="ex-menes">Exámenes</h2><p>Sabemos que muchos están esperando los exámenes al final de cada módulo. Ya casi están listos.</p><p>También estamos creando un entorno de examen propio, que busca cuidar tu privacidad y mantener la integridad académica.</p><h2 id="-qu-sigue">¿Qué sigue?</h2><p>Ahora estamos trabajando en los módulos de CSS Libraries y TypeScript. También vienen módulos de Python.</p><p>En los próximos meses lanzaremos un montón de cosas nuevas.</p><p>Un adelanto de los próximos proyectos:</p><ul><li>crea tu propio personaje de RPG</li><li>un juego de cartas coleccionables</li><li>un validador de datos médicos</li><li>y mucho más 🏕️</li></ul><h2 id="-te-gustar-a-ayudar">¿Te gustaría ayudar?</h2><p>Si quieres ser parte del desarrollo del plan Full Stack, tenemos muchas formas de contribuir. Mira los <a href="https://github.com/freecodecamp/freecodecamp/issues">issues abiertos en nuestro repo de GitHub</a>.</p><p>Empieza leyendo <a href="https://contribute.freecodecamp.org/intro/">la guía de bienvenida para contribuidores</a> y <a href="https://discord.gg/KVUmVXA">únete a nuestro Discord si tienes preguntas</a>.</p><p>Nos emociona verte avanzar con todo este contenido nuevo. ¡Sigue creciendo tus habilidades!</p><p>¡Feliz programación! 💜</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo llevar a cabo una gran revisión de sprint -consejos accionables para desarrolladores y equipos ]]>
                </title>
                <description>
                    <![CDATA[ En mis veinte años de experiencia como ingeniero de software, he estado en muchas revisiones de sprint. Las he visto hacer bien y he visto que a veces se las usa para que las personas hagan un alto en una sala de reuniones por un par de horas cada sprint. ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/how-to-run-a-great-sprint-review-actionable-tips-for-developers-and-teams-como-llevar-a-cabo-un/</link>
                <guid isPermaLink="false">682280711822d2047c6e5812</guid>
                
                    <category>
                        <![CDATA[ Agile Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Constanza Areal ]]>
                </dc:creator>
                <pubDate>Fri, 20 Jun 2025 15:12:58 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2025/06/fcc7407a-3b08-493f-a704-661eed12f369.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/how-to-run-a-great-sprint-review-actionable-insights/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Run a Great Sprint Review – Actionable Tips for Developers and Teams</a>
      </p><p>En mis veinte años de experiencia como ingeniero de software, he estado en muchas revisiones de sprint. Las he visto hacer bien y he visto que a veces se las usa para que las personas hagan un alto en una sala de reuniones por un par de horas cada sprint.</p><p>Realizada de manera correcta, la revisión de sprint puede ser la clave para mantener al día tu proyecto. Por el contrario, las revisiones de sprint hechas de forma incorrecta son peor que el tiempo malgastado en ellas, ya que disminuyen la confianza de las personas en la metodología Scrum.<br><br>En este artículo, te voy a guiar a través de algunas ideas y el paso a paso para hacer que tu revisión de sprint tenga más valor.</p><h2 id="-qu-es-una-revisi-n-de-sprint"><strong><strong>¿Qué</strong> es una revisión de sprint?</strong></h2><p>Una <strong>revisión de sprint</strong> es una sesión corta que se realiza al final de cada sprint, usualmente cada un par de semanas.</p><p>Esta sesión permite al equipo mostrar el trabajo realizado a través de demos, obtener feedback y decidir qué es lo que sigue.</p><p>Al hacerse de forma correcta, la revisión de sprint muestra progreso y crea confianza a las personas que tienen un interés en el producto y el proyecto.</p><p>Por supuesto, es posible decirles a los interesados que el proyecto está al día, pero si pueden verlo con sus propios ojos durante la revisión de sprint, estarán más contentos.</p><h2 id="-qui-n-deber-a-estar-en-la-revisi-n"><strong>¿Quién debería estar en la revisión?</strong></h2><p>En resumen, cualquier persona que así lo quiera. Cualquier persona que sea parte interesada en cualquier capacidad puede y debería estar presente en estas reuniones.</p><p>El propio equipo scrum se encuentra en el centro de la reunión, pero también pueden estar los de Ventas, los subgerentes generales, otros equipos scrums y project managers.</p><p>Si alguien tiene algo para aportar al proyecto o cree que puede beneficiarse por aprender algo del proyecto, deberían asistir,</p><h2 id="-qu-hacer-antes-de-la-revisi-n-de-sprint"><strong>¿Qué hacer antes de la revisión de sprint?</strong></h2><p>Para que la reunión se lleve a cabo sin ningún problema, es necesario asegurarse que quienes vayan a presentar estén preparados y con una demo lista para mostrar.</p><p>Por experiencia propia, las demos en vivo no funcionan. Algunas sí, pero en su mayoría no.</p><p>Puedes decirles a quienes tengan una demo que al menos se graben mostrando el flujo de trabajo antes de la reunión. De esta manera, si insisten en hacer una demo en vivo, van a tener un video grabado a modo de resguardo si todo sale mal.</p><p>También deberías tener una lista con las personas que van a presentar y en qué orden. Agrúpalas por similitud del tema. No importa quién presenta y cuándo lo hace mientras que las presentaciones estén agrupadas por tema. </p><p>Por ejemplo, si tienes seis ingenieros - dos trabajando en una página de inicio de sesión, dos más trabajando en servicio back-end nuevo y otros dos trabajando en la performance de la base de datos- asegúrate que en tu lista de presentadores estas tres areas distintivas estén juntas.</p><p>Si hay dos demos que son similares, puedes explicar el contexto una sola vez y después correr las demos una seguida de la otra. Se eficiente, ya que tienes a muchas personas en esta reunión y todos saben que <a href="https://calculatorlord.com/meeting-cost-calculator/">las reuniones son costosas</a>.</p><h2 id="durante-la-revisi-n"><strong>Durante la revisión</strong></h2><p>Los ingenieros de software que hayan trabajado en alguna parte del proyecto harán la presentación a los otros miembros del equipo scrum (Producto, QA y otros) y también a los interesados que estén en la reunión.</p><p>Después de la presentación de cada ingeniero, cualquiera de los presentes en la reunión puede hacer preguntas o comentarios sobre el trabajo realizado o la presentación.</p><p>Estas preguntas pueden ir desde detalles sobre el proyecto para quienes no conozcan, lo conozcan en su totalidad o pueden ser preguntas sobre porque se decidió seguir un curso de acción en particular.</p><p>Desde Producto o miembros de la audiencia que estén más enfocados en la parte del negocio pueden dar feedback sobre si lo que fue presentando coincide con la intención de negocio de lo que se pidió entregar.</p><p>Una vez que todas las preguntas y comentarios son respondidos, el próximo ingeniero presentará su trabajo.</p><p>En una revisión de sprint, si bien todos pueden hablar, generalmente solo lo hacen los ingenieros que presentan. Entonces, el ingeniero presenta sobre el trabajo que realizaron y luego Producto, QA, Ba y los demás pueden dar feedback y hacer preguntas específicas sobre lo que el ingeniero presentó.</p><h2 id="revisi-n-de-ejemplo"><strong>Revisión de ejemplo</strong></h2><p>Demos una mirada a una revisión de ejemplo y a lo que podría contener.</p><p>Vamos a usar el ejemplo de los seis ingenieros de más arriba: dos trabajando en la página de inicio de sesión, dos trabajando en un servicio back-end y dos trabajando en la performance de la base de datos. En ese caso, la lista de presentadores se ve así:</p><p>Presentaciones</p><ol><li>Larry: <em>El usuario supera la cantidad máxima de intentos de inicio de sesión y la cuenta se bloquea después del quinto intento incorrecto. El usuario tiene que pedir un nuevo password antes de poder iniciar sesión de nuevo.</em></li><li>David: <em>El usuario hace clic en el enlace de contraseña olvidada y se emvia un enlace a su correo electrónico. El usuario hace clic en el enlace y puede restablecer su contraseña.</em></li><li>Premilla: <em> Un servicio de reporte consume el evento "Usuario excede la cantidad máxima de intentos de inicio de sesión" y lo publica en el tablero de reportes.</em></li><li>Akshat: <em>Un servicio de reporte consume el evento "Usuario hace clic en el enlace de restablecimiento de contraseña" y lo publica en el tablero de reportes.</em></li><li>Olga: <em>Un usuario administrador puede ver el tablero durante un mes y cargar el reporte dentro de los 2 segundos.</em></li><li>Trevor: <em> Un usuario administrar puede ver los eventos de varios usuarios en un mismo tablero y cargar el reporte dentro de los 2 segundos.</em></li></ol><p>Como puedes ver, las dos presentaciones sobre la página de inicio de sesión de usuario se hicieron primero, después las dos presentaciones sobre los servicios de reportes, y por último las dos presentaciones sobre la velocidad de respuesta de la base de datos. Esto requiere un menor cambio de contexto para los miembros de la audiencia y también que es posible dar el contexto en la primera de las dos presentaciones agrupadas (esto es, las presentaciones 1, 3 y 5).</p><p>Después de la presentación 1 (realizada por Larry), Producto puede preguntarle por qué eligió cinco intentos como el número máximo de intentos antes de que se bloquee la cuenta. Larry puede tener o no una respuesta. Producto puede pedirle un seguimiento sobre este tema e investigar cuál es el estándar de la industria y aplicar la misma lógica que Larry o dejarlo así.</p><p>Después de las seis presentaciones, puede haber preguntas, comentarios o pedidos de cambio de cualquier persona de la audiencia. Usualmente, esto va a precipitar una conversación entre los presentes sobre cuál puede ser la mejor manera de realizar estos cambios.</p><p>Por ejemplo, de nuevo en el ejemplo de Larry, algunos podrían sugerir que ni se debería usar un nombre de usuario y contraseña, sino una dirección de correo electrónico y un <a href="https://www.pingidentity.com/en/resources/blog/post/what-is-magic-link-login.html">enlace mágico</a>. Esto es lo bueno de la revisión. Tenemos a un montón de gente inteligente en la habitación contribuyendo con su propia experiencia y sabiduría. </p><h2 id="consejos-accionables"><strong>Consejos accionables</strong></h2><p>Here are some tips for what to show, how to show it, and what actually matters during the meeting itself. &nbsp;Aquí tienes un par de consejos sobre qué mostrar, cómo mostrarlo, y lo que realmente importa durante la reunión en sí.</p><h3 id="1-mostrar-entregables-reales"><strong><strong>1. </strong>Mostrar entregables reales</strong></h3><p>Primero, siempre muestra software en funcionamiento o demos que funcionen en vez de diapositivas estáticas. Por ejemplo, si estuviste trabajando en una nueva función de inicio de sesión, haz una demostración en vivo. Esto hace que la revisión sea más auténtica y muestre avances tangibles.</p><h3 id="2-alienta-el-feedback"><strong><strong>2. </strong>Alienta el feedback</strong></h3><p>Segundo, invita a la audiencia a hacer preguntas. Recuérdale a las partes interesadas que sus ideas pueden ayudar a desarrollar nuevas funcionalidades. Anota sus sugerencias y confírmales si alguno de los cambios irán de inmediato a tu lista de pendientes.</p><h3 id="3-s-claro-y-mantente-enfocado"><strong><strong>3. </strong>Sé claro y mantente enfocado</strong></h3><p>Tercero, mantén un formato simple. Por ejemplo, empieza con un pantallazo general del objetivo del sprint, ve a las demostraciones y termina con un debate corto sobre los próximos pasos.</p><p>Evita entrar en profundidad sobre los detalles técnicos. Si el tema requiere más tiempo, organiza una reunión de seguimiento.</p><h3 id="4-coordina-los-pr-ximos-pasos-"><strong><strong>4. </strong>Coordina los próximos pasos.</strong></h3><p>Seguido de esto, actualiza tu lista de pendientes con base en lo aprendido en la reunión. Si una funcionalidad requiere un ajuste extra, agrega la tarea de manera inmediata.</p><p>Esto ayuda a mantener informado a todo el equipo sobre el plan para el siguiente sprint. </p><h3 id="5-mantenla-corta-y-termina-en-horario"><strong><strong>5. </strong>Mantenla corta y termina en horario</strong></h3><p>En la reunión deberías revisar el trabajo del equipo scrum de un solo sprint (dos o tres semanas)</p><p>Si la reunión se hace demasiado larga, esto puede significar que tu equipo scrum es demasiado grande (en ese caso, deberías pensar en dividir el equipo en <a href="https://namegenerators.online/scrum-team-name-generator/">grupos más pequeños)</a> o que no estás siendo eficiente con tus demostraciones.</p><p>El tiempo y la atención de las personas son costosos y escasos. Mantén las reuniones a un <a href="https://en.wikipedia.org/wiki/Basic_rest%E2%80%93activity_cycle">ciclo ultradriano</a>. Personalmente, trato de limitar todas las reuniones a un máximo de 90 minutos.</p><p>Por último, establece un límite firme para la reunión así los participantes se sienten confiados al dar su feedback sin alargar el debate.</p><p> Terminar a horario significa respetar el calendario de los demás y mantener al equipo descansado y preparado para el próximo sprint.</p><h2 id="en-conclusi-n"><strong>En conclusión</strong></h2><p>Una revisión de sprint les da a las partes interesadas una mirada real sobre el trabajo realizado, las mantiene alineadas sobre los próximos pasos, y permite obtener el feedback que necesitas para hacer crecer tu producto de forma efectiva.</p><p> Si te enfocas en mostrar avances reales, tener un dialogo abierto y mantienes las cosas de forma concisa, verás una mejoría en como tu equipo entrega valor.</p><p><em>En su tiempo libre, Ben escribe su blog de tecnología "</em><a href="https://justanothertechlead.com/"><em>Just Another Tech Lead</em></a><em>" y administra un sitio donde crea calculadoras gratuitas llamado "</em><a href="https://calculatorlord.com/"><em>CalculatorLord.com</em></a><em>".</em></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Next.js vs React : Diferencias y cómo elegir el adecuado para tu proyecto ]]>
                </title>
                <description>
                    <![CDATA[ Como desarrollador, hay muchas herramientas, lenguajes, frameworks y paquetes de código abierto que tienes que aprender para que tu trabajo se facilite y sea sencillo (aunque el trayecto no es sencillo, lo lograrás). Algunas de estas herramientas, lenguajes, o frameworks son usados diariamente por miembros de la comunidad y pueden ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/next-js-vs-react-diferencias-y-como-elegir-el-adecuado-para-tu-proyecto/</link>
                <guid isPermaLink="false">67ff2616e738c10479d3f336</guid>
                
                    <category>
                        <![CDATA[ NextJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ csr ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Fri, 20 Jun 2025 15:06:15 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2025/04/Captura-desde-2025-04-16-00-38-46.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Como desarrollador, hay muchas herramientas, lenguajes, frameworks y paquetes de código abierto que tienes que aprender para que tu trabajo se facilite y sea sencillo (aunque el trayecto no es sencillo, lo lograrás).</p><p>Algunas de estas herramientas, lenguajes, o frameworks son usados diariamente por miembros de la comunidad y pueden padecer cambios fundamentales en cómo son implementados o escritos con el tiempo.</p><p>En este artículo, exploraremos dos tecnologías populares de JavaScript, Next.js y React.js, comparando sus diferencias claves, examinando sus fortalezas, y ofreciendo perspectivas para ayudar a los desarrolladores a elegir la mejor opción para sus proyectos.</p><h2 id="tabla-de-contenido"><strong><strong>Tabl</strong>a<strong> </strong>de<strong> </strong>Contenido</strong></h2><!--kg-card-begin: markdown--><ul>
<li><a href="#entendiendo-react">Entendiendo React</a>
<ul>
<li><a href="#renderizado-lado-cliente">Renderizado del lado del Cliente</a></li>
<li><a href="#casos-uso-react-desarrollo-web">Casos de uso para React en Desarrollo Web</a></li>
</ul>
</li>
<li><a href="#explorando-nextjs">Explorando Next.js</a>
<ul>
<li><a href="#renderizado-lado-servidor">Renderizado del lado del Servidor</a></li>
<li><a href="#casos-uso-nextjs-desarrollo-web">Casos de uso para Next.js en Desarrollo Web</a></li>
</ul>
</li>
<li><a href="#diferencia-clave-nextjs-react">Diferencias claves entre Next.js y React</a>
<ul>
<li><a href="#metodos-renderizado">Métodos de renderizado: lado del cliente vs. lado del servidor</a></li>
<li><a href="#consideraciones-rendimiento">Consideraciones de rendimiento</a></li>
<li><a href="#implicaciones-seo">Implicaciones sobre SEO</a></li>
<li><a href="#escalabilidad-complejudad-proyecto">Escalabilidad y complejidad del proyecto</a></li>
</ul>
</li>
<li><a href="#cuando-usar-react-nextjs">Cuándo usar React o Next.js</a>
<ul>
<li><a href="#cuando-usar-react">Cuándo usar React</a></li>
<li><a href="#cuando-usar-nextjs">Cuándo usar Next.js</a></li>
</ul>
</li>
<li><a href="#conclusion">Conclusión</a></li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><h2 id="entendiendo-react">Entendiendo React</h2><!--kg-card-end: html--><p>Frecuentemente nos confundimos en cuanto si React es un framework de JavaScript o no, pero acá está la respuesta a esa pregunta. React no es un framework de JavaScript, sino una librería. Ahora, estas dos terminologías son intercambiables con frecuencia o mal usadas, pero los explicaré en breve.</p><p>Una librería es una colección de código ya escrito que puede ser re-usado o llamarse cuando construyes tu proyecto.</p><p><strong>Ejemplo</strong>: Imagina una biblioteca en donde vas a estudiar. Los libros ya están disponibles en los estantes – simplemente agarras uno que necesitas y comienzas a leer. De forma similar, en programación, una librería (o biblioteca) provee herramientas ya terminadas que puedes usar en tu proyecto sin tener que empezar de cero.</p><p>Por otro lado, un framework es como una estructura ya hecha que te ayuda a construir tu proyecto. Te da una fundación sólida para trabajar, de forma que no tengas que comenzar desde cero o escribir código repetitivo. En cambio, te enfocas en agregar tus propias características y lógica, mientras que el framework se encarga de ejecutar cosas en el momento exacto y de la forma correcta.</p><p><strong>Ejemplo</strong>: Imagina que un framework es como una casa en construcción donde los muros, el fundamento, y el techo ya están construidos. Todo lo que necesitas hacer es decidir cómo diseñar el interior – cómo elegir los muebles, la pintura y las decoraciones. El framework maneja el trabajo pesado, cómo asegurar que la casa se mantenga fuerte, mientras te enfocas en hacer lo tuyo. De forma similar, en programación, el framework provee la estructura, y tu agregas tu lógica personalizada para completar el proyecto.</p><p>Con eso fuera del camino, continuemos.</p><p>React es una de las librerías de JavaScript más populares usadas por los desarrolladores para construir interfaces de usuario rápidos, interactivos y confiables. Es una librería declarativa que ayuda a los desarrolladores crear aplicaciones web basado en componentes. Facebook desarrolló esta librería en 2011 y ha sido tendencia desde entonces.</p><p>Usualmente, cuando se escribe código JavaScript, creamos un archivo con la extensión <code>js</code>. Por ejemplo: <code>App.js</code>, <code>script.js</code>, y así sucesivamente. En React creamos un archivo con la extensión <code>jsx</code>. Eso es: <code>index.jsx</code>, <code>Home.jsx</code>, y así sucesivamente. El <code>jsx</code> es una extensión de React que te permite escribir un código de JavaScript que se parece a HTML. La sintaxis, cuando se ejecuta, pasa a través de pre-procesadores/transpiladores los cuales transforman el código parecido a HTML a código estándar de JavaScript.</p><p>En el corazón de todas las aplicaciones de React están los componentes. Los componentes son trozos de interfaces de usuario (UI) los cuales son hechos de forma independiente y pueden ser re-usados en partes distintas en tu proyecto. Distintos componentes pueden ser hechos de forma separada y luego se juntan para formar una interfaz de usuario compleja (UI).</p><p><strong>Nota</strong>: Cada aplicación de React tiene al menos un componente, comúnmente referido como el componente raíz. Este componente representa la aplicación entera. Dentro del componente raíz, con frecuencia hay otros componentes, conocidos como componentes hijos, que ayudan a estructurar y manejar distintas partes de la aplicación.</p><p>Acá hay una representación estructural de los componentes raíz e hijos.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732758941272/17c6b471-b2a7-40ae-83f8-4e215a50c853.png" class="kg-image" alt="structural representation of root and child components" width="939" height="753" loading="lazy"></figure><p>De la imagen de arriba, puedes entender con claridad de qué tratan los componentes. <code>App</code> es el componente raíz, y dentro del componente raíz tenemos los componentes hijos: <code>Navbar</code>, <code>Profile</code>, <code>Blog</code> y <code>Footer</code>. Los hijos componentes pueden ser re-usados en otras páginas del proyecto sin tener que re-escribir el código nuevamente.</p><!--kg-card-begin: html--><h3 id="renderizado-lado-cliente">Renderizado del lado del cliente</h3><!--kg-card-end: html--><p>El renderizado del lado del cliente (CSR – Client Side Rendering en inglés) es una técnica común, especialmente en librerías como React y frameworks como Vue.js, Angular, y así sucesivamente. Aquí, el navegador descarga y procesa los archivos de JavaScript para renderizar dinámicamente el contenido directamente en el dispositivo del usuario. Con CSR, las páginas web se generan de forma dinámica, y cualquier actualización o cambio al código se aplica sin requerir una recarga completa de página. Solamente las partes específicas que han cambiado se actualizan, asegurando una experiencia de usuario fluida y eficiente.</p><p>Por lo tanto, en CSR, la lógica y la estructura de la página web se maneja por el cliente (navegador) y una recarga completa de página se muestra.</p><p>Para ayudarte a entender CSR, <a href="https://www.freecodecamp.org/news/rendering-patterns/#heading-single-page-applications-spas-with-client-side-rendering-csr">he agregado un artículo aquí</a>. </p><!--kg-card-begin: html--><h3 id="casos-uso-react-desarrollo-web">Casos de uso para React en Desarrollo Web</h3><!--kg-card-end: html--><p>Desde que React se volvió la opción preferida de muchos desarrolladores, su flexibilidad lo ha hecho conveniente para un amplio rango de casos de uso en desarrollo web. Aquí hay unos pocos casos:</p><ul><li><strong>Aplicaciones de una Sola Página<strong> (SPAs):</strong></strong> Cuando hablamos sobre SPAs, no nos referimos realmente que tu aplicación web tiene solamente una página, puede tener múltiples páginas. En SPAs, tus archivos de aplicación web (HTML, CSS, JS) se generan una vez en tu página web y cuando las actualizaciones subsecuentes se realizan en el archivo, tu página no tendrá que recargar completamente. Este enfoque ayuda a asegurar una transición más rápida, reduce la carga en el servidor y mejorar la experiencia general de usuario.</li><li><strong>Interfaces de Usuario <strong>Interactiv</strong>os<strong>:</strong></strong> React es idóneo para construir interfaces de usuarios interactivos, de vez en cuando, pasa por actualizaciones dinámicas basadas en las acciones de los usuarios. Ejemplos son formularios en línea, paneles de control, sitios web (sitios web de comercio electrónico), y así sucesivamente.</li><li><strong>Aplicaciones Multiplataforma<strong>:</strong></strong> Tener conocimientos de React viene a la mano cuando se construyen aplicaciones móviles, simplificando la conexión entre aplicaciones web y aplicaciones móviles. Herramientas como React Native te ayudan a lograr este proceso.</li></ul><!--kg-card-begin: html--><h2 id="explorando-nextjs">Explorando Next.js</h2><!--kg-card-end: html--><p>Next.js es un framework popular basado en React usado para construir aplicaciones web con el uso de componentes de React. Next.js provee estructura adicional, características, y optimización para tu aplicación web.</p><p>A diferencia de React, Next.js soporta renderizado del lado del servidor (SSR), por lo que las solicitudes se procesan y se generan desde el servidor y luego mostrado en el navegador (cliente).</p><!--kg-card-begin: html--><h3 id="renderizado-lado-servidor">Renderizado del lado del Servidor</h3><!--kg-card-end: html--><p>El Renderizado del lado del servidor (SSR - en inglés "Server Side-Rendering") es una técnica en el desarrollo web donde un servidor genera el HTML para una página web en el servidor y lo envía al navegador (cliente). En otras palabras, el servidor maneja las estructuras y la lógica de la página y muestra una página totalmente renderizada en la pantalla.</p><p>En el renderizado del lado del servidor, una solicitud se envía primero al servidor (cliente), luego el servidor comienza a procesar la solicitud y cuando termina de procesar la solicitud, ejecuta la solicitud al generar y muestra un archivo HTML con el contenido en el navegador (lado del cliente). Cuando un cambio se hace o una nueva página se solicita, una nueva solicitud se envía nuevamente al servidor y es procesado nuevamente – un nuevo archivo HTML y totalmente renderizado será generado y mostrado en el navegador (cliente).</p><p>Para una mejor comprensión de CSR y SSR, agregué un <a href="https://youtu.be/-JXUaydU1J0?si=U3PrqicrIJoLYOM9">vídeo de YouTube aquí</a>.</p><!--kg-card-begin: html--><h3 id="casos-uso-nextjs-desarrollo-web">Casos de Uso para Next.js en el desarrollo web</h3><!--kg-card-end: html--><ul><li><strong>Aplicaciones de una Sola Página<strong> (SPAs):</strong></strong> Next.js puede ser usado en la creación de aplicaciones de una sola página, similar a React.</li><li><strong>Optimizado para <strong>SEO:</strong></strong> Next.js ayuda en crear sitios web optimizados para SEO al renderizar un archivo HTML en el servidor y entregarlo al navegador. Esto mejora la visibilidad en el motor de búsquedas, incrementando las chances que tu sitio web aparezca en la parte superior de los resultados de búsqueda.</li><li><strong>Plataformas de <strong>Multi-Us</strong>uarios<strong>:</strong></strong> Debido a la habilidad de Next.js de manejar el enrutamiento dinámico, el manejo de API, y así sucesivamente, es fácil de crear aplicaciones que sirven para diversos propósitos.</li></ul><!--kg-card-begin: html--><h2 id="diferencia-clave-nextjs-react">Diferencia clave entre Next.js y React</h2><!--kg-card-end: html--><!--kg-card-begin: html--><h3 id="metodos-renderizado">Métodos de renderizado: lado del cliente vs. lado del servidor</h3><!--kg-card-end: html--><p>Cuando hablamos sobre el método de renderizado en React, React se base principalmente en el método de renderizado del lado del cliente (CSR). Por lo tanto tanto la lógica como la estructura de la página web será manejado por el navegador (cliente). Aunque este método se usa comúnmente, tiene algunos efectos secundarios como carga de página inicial más lenta.</p><p>Next.js por el otro lado, soporta tanto SSR como CSR ya que fue construido por encima de React. Las páginas web se renderizan en el servidor y tanto la lógica como la estructura de la página se manejan por el servidor. Esto permite una carga más rápida de la página web y también mejora el SEO.</p><!--kg-card-begin: html--><h3 id="consideraciones-rendimiento">Consideraciones de rendimiento</h3><!--kg-card-end: html--><p>En términos de consideraciones de rendimiento, Next.js se prefiere frecuentemente porque ofrece múltiples opciones de renderizado, incluyendo renderizado del lado del servidor (SSR), generación de sitios estáticos (SSG), Regeneración estático incremental (ISR), y renderizado del lado del cliente (CSR). En contraste, React primariamente provee un enfoque de renderizado único: renderizado del lado del cliente.</p><!--kg-card-begin: html--><h3 id="implicaciones-seo">Implicaciones de SEO</h3><!--kg-card-end: html--><p>React está menos optimizado para SEO porque los motores de búsqueda puede tener dificultades al indexar contenido que requiere ejecución de JavaScript para renderizar.</p><p>Del otro lado, Next.js está más optimizado para SEO que React porque renderiza contenido en el servidor, provee HTML totalmente renderizado para los motores de búsqueda para facilitar el indexado.</p><!--kg-card-begin: html--><h3 id="escalabilidad-complejudad-proyecto">Escalabilidad y complejidad del Proyecto</h3><!--kg-card-end: html--><p>En términos de escalabilidad y complejidad del proyecto, Next.js es generalmente mejor que React. Next.js provee funciones integradas que mejoran la escalabilidad de tu proyecto. Estos incluyen:</p><ul><li>Renderizado del lado del servidor (SSR) y generación de sitios estáticos (SSG) para mejor rendimiento y SEO.</li><li>Una función de rutas API incorporada para crear funciones serverless sin problemas.</li><li>Un sistema de enrutamiento basado en archivos que simplifica la organización de proyectos más grandes.</li></ul><p>En contraste, con React, eres responsable de configurar y mantener la estructura para la escalabilidad. Para proyectos más grandes, esto frecuentemente requiere agregar herramientas adicionales tales como:</p><ul><li>Librerías de gestión de estado (por ejemplo, Redux, Recoil, y así sucesivamente)</li><li>Librerías de enrutamiento (por ejemplo, React Router)</li></ul><p>Estas herramientas son necesarias para mejorar la escalabilidad de React y abarcar la complejidad del proyecto, pero también incrementan la carga y esfuerzo necesario para configurar y gestionar la aplicación.</p><p>En resumen, aquí una tabla que lo desglosa:</p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th>Factores</th>
<th>React</th>
<th>Next.js</th>
</tr>
</thead>
<tbody>
<tr>
<td>Escalabilidad</td>
<td>Es posible pero para incrementar la escalabilidad, requiere herramientas adicionales y una configuración personalizada.</td>
<td>Es escalable y ya tiene herramientas integradas que incrementan la escalabilidad.</td>
</tr>
<tr>
<td>Rendimiento</td>
<td>Provee solamente una opción de renderizado el cual es el renderizado del lado del cliente (CSR).</td>
<td>Ofrece múltiples opciones de renderizado, incluyendo SSR, SSG, ISR, y CSR.</td>
</tr>
<tr>
<td>SEO</td>
<td>No está optimizado para el SEO porque a los motores de búsquedas les podría costar indexar el contenido que requiere ejecución de JavaScript para renderizar.</td>
<td>Está más optimizado para el SEO que React porque renderiza el contenido en el servidor, proveyendo HTML totalmente renderizado para los motores de búsqueda para indexar más fácilmente.</td>
</tr>
<tr>
<td>Caso de uso</td>
<td>Mayormente usado en proyectos más pequeños o únicos.</td>
<td>Mayormente usado en proyectos de gran escala y mejora el rendimiento el SEO.</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><h2 id="cuando-usar-react-nextjs">Cuándo usar React o Next.js</h2><!--kg-card-end: html--><p>Elegir la herramienta correcta para tu proyecto depende únicamente en la complejidad de la solución del proyecto que estás construyendo. Mientras que React y Next.js están relacionados cercanamente, cada uno tiene sus fortalezas y casos de uso óptimos.</p><!--kg-card-begin: html--><h3 id="cuando-usar-react">Cuándo usar React</h3><!--kg-card-end: html--><p>Aquí hay algunos casos donde es mejor usar React para tu proyecto:</p><ul><li>Cuando construyes aplicaciones altamente interactivas.</li><li>Cuando tu proyecto requiere manejo manual de enrutamiento, gestión de estado o/e integración de API.</li><li>Cuando tu proyecto requiere renderizado del lado del cliente (CSR)</li></ul><!--kg-card-begin: html--><h3 id="cuando-usar-nextjs">Cuándo usar Next.js</h3><!--kg-card-end: html--><p>Aquí hay algunos casos donde es mejor usar Next.js:</p><ul><li>Cuando tu proyecto requiere un mejor SEO.</li><li>Cuando tu proyecto requiere renderizado del lado del servidor.</li><li>Cuando tu proyecto requiere que construyas APIs junto con tu código de frontend.</li><li>Cuando construyes sitios web basado en contenido como blogs o sitios e-commerce. Debido a su uso de renderizado del lado del servidor, ayuda en mejorar los tiempos de carga de los contenidos en la página.</li><li>Next.js se usa mejor cuando quieres optimizar imágenes en tu proyecto.</li></ul><!--kg-card-begin: html--><h2 id="conclusion">Conclusión</h2><!--kg-card-end: html--><p>A este punto creo que tienes una clara comprensión de React y de Next.js, los conceptos del renderizado del lado del servidor y del lado del cliente, los casos de uso tanto para React como para Next.js, y las diferencias claves entre ellos.</p><p>Gracias por tomarte el tiempo de leer este artículo. Espero que lo hayas encontrado útil.</p><p>Feliz codificación.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Aprende Godot - Curso desde cero ]]>
                </title>
                <description>
                    <![CDATA[ Godot es un motor de videojuegos de código abierto, ligero y poderoso. Este curso te enseñará a usar Godot para hacer realidad tus ideas de videojuegos. Acabamos de publicar un curso en el canal de YouTube de freeCodeCamp.org en español [https://www.youtube.com/freecodecampespanol] que te guiará paso a paso por los fundamentos ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/aprende-godot-curso-desde-cero/</link>
                <guid isPermaLink="false">683f052be578ab047921a872</guid>
                
                    <category>
                        <![CDATA[ Godot ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Estefania Cassingena Navone ]]>
                </dc:creator>
                <pubDate>Wed, 04 Jun 2025 04:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2025/06/Godot-3.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Godot es un motor de videojuegos de código abierto, ligero y poderoso. Este curso te enseñará a usar Godot para hacer realidad tus ideas de videojuegos.</p><p>Acabamos de publicar un curso en el <a href="https://www.youtube.com/freecodecampespanol">canal de YouTube de freeCodeCamp.org en español</a> que te guiará paso a paso por los fundamentos de Godot. Aprenderás los conceptos clave que necesitas para comenzar.</p><p>Luis Canary creó este curso. Él es el Programador Principal de Gameplay en Pendulo Studios, una empresa de desarrollo de videojuegos con sede en Madrid. Ha dado cursos en universidades, escuelas y empresas y le encanta compartir su pasión por el desarrollo de videojuegos en su canal de YouTube.</p><p>Antes de adentrarnos en el contenido del curso, veamos qué es Godot y por qué deberías aprenderlo.</p><h2 id="-qu-es-godot"><strong>¿Qué es Godot?</strong></h2><p>Godot es un motor de videojuegos de código abierto que te permite crear videojuegos 2D y 3D.</p><p>Se ha vuelto muy popular en los últimos años por sus funcionalidades robustas que lo convierten en una gran herramienta en el mundo del desarrollo de videojuegos, sin las grandes exigencias de recursos de otros motores de videojuegos. Esto lo convierte en una opción práctica para creadores que buscan eficiencia y alto rendimiento.</p><p>Su licencia de código abierto (<em>open source</em>) es excelente para desarrolladores independientes y equipos pequeños, ya que no tiene tarifas de licencia. Tendrás todos los derechos y control total sobre los juegos y proyectos que crees con Godot.</p><p>Esta flexibilidad contribuye a que el desarrollo de videojuegos sea más accesible para todos.</p><p>Godot también tiene su propio lenguaje de scripting fácil de usar y un sistema basado en nodos. Su lenguaje de scripting, llamado GDScript, es muy similar a Python y es fácil de aprender para principiantes. Su sistema intuitivo basado en nodos simplifica el proceso de crear juegos 2D y 3D.</p><p>Al aprender Godot, estarás dando tus primeros pasos hacia una carrera en el desarrollo de videojuegos.</p><p><strong>💡 Tip:</strong> Este curso es perfecto para cualquiera que desee iniciarse en el mundo del desarrollo de videojuegos. Enseña GDScript con un ejemplo práctico.</p><h2 id="curso-de-godot-en-espa-ol"><strong>Curso de Godot en español</strong></h2><p>Genial. Ahora que ya sabes más sobre Godot, veamos lo que aprenderás durante el curso:</p><ul><li>Introducción</li><li>Qué es Godot</li><li>Descargar e instalar Godot</li><li>Iluminación</li><li>Materiales y física</li><li>Interfaz de usuario</li><li>Interfaz de usuario</li><li>Movimiento</li><li>Jugador</li><li>Primer script</li><li>Actualizar proyecto</li><li>Cambiar inputs proyecto 3D</li><li>Plataformas 3D y respawn player</li><li>Recolectar monedas</li><li>Rotación de cámara 3D</li><li>Modelo de personaje y animaciones</li><li>Música y sonidos</li><li>Exportar videojuego .exe</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2025/06/image.png" class="kg-image" alt="image" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2025/06/image.png 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2025/06/image.png 1000w, https://www.freecodecamp.org/espanol/news/content/images/size/w1600/2025/06/image.png 1600w, https://www.freecodecamp.org/espanol/news/content/images/2025/06/image.png 1920w" sizes="(min-width: 720px) 720px" width="1920" height="1080" loading="lazy"><figcaption>Fundamentos de Godot. Movimiento y rotación.</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2025/06/image-1.png" class="kg-image" alt="image-1" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2025/06/image-1.png 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2025/06/image-1.png 1000w, https://www.freecodecamp.org/espanol/news/content/images/size/w1600/2025/06/image-1.png 1600w, https://www.freecodecamp.org/espanol/news/content/images/2025/06/image-1.png 1920w" sizes="(min-width: 720px) 720px" width="1920" height="1080" loading="lazy"><figcaption>Proyecto.</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2025/06/image-2.png" class="kg-image" alt="image-2" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2025/06/image-2.png 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2025/06/image-2.png 1000w, https://www.freecodecamp.org/espanol/news/content/images/size/w1600/2025/06/image-2.png 1600w, https://www.freecodecamp.org/espanol/news/content/images/2025/06/image-2.png 1920w" sizes="(min-width: 720px) 720px" width="1920" height="1080" loading="lazy"><figcaption>Agregando un personaje.</figcaption></figure><p>Al completar el curso sabrás los fundamentos Godot y podrás comenzar a desarrollar videojuegos.</p><p>Te invitamos a ver el curso en el canal de YouTube de <a href="https://www.youtube.com/freecodecampespanol">freeCodeCamp.org en español</a>:</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/7898KcoAmLE?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="Aprende Godot - Curso completo desde cero" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><p>✍️ Curso creado por Luis Canary.</p><ul><li>YouTube: <a href="https://www.youtube.com/channel/UC_XaEmy0Rz49GkrhtpzqWlw" rel="noopener noreferrer nofollow">@LuisCanary</a></li><li>Instagram: <a href="https://www.instagram.com/luiscanary_/" rel="noopener noreferrer nofollow">@luiscanary_</a></li><li>Twitter: <a href="https://x.com/luiscanary" rel="noopener noreferrer nofollow">@luiscanary</a></li><li>TikTok: <a href="https://www.tiktok.com/@luiscanary?lang=es" rel="noopener noreferrer nofollow">@luiscanary</a></li><li>Twitch: <a href="https://www.twitch.tv/luiscanary" rel="noopener noreferrer nofollow">LuisCanary</a></li><li>Discord: <a href="https://discord.com/invite/BEQ2UZY" rel="noopener noreferrer nofollow">Invitación</a></li><li>Facebook: <a href="https://www.facebook.com/LuisCanaryy/" rel="noopener noreferrer nofollow">LuisCanaryy</a></li></ul> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Construir un juego de memoria en React ]]>
                </title>
                <description>
                    <![CDATA[   ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/build-un-juego-de-memoria/</link>
                <guid isPermaLink="false">67edc82d9118ef04ee3e70f3</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Isabela Mena Bueno ]]>
                </dc:creator>
                <pubDate>Thu, 08 May 2025 15:40:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2025/06/a0102079-c5ee-4eaa-8757-cf4d7740033c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/build-a-memory-game-in-react/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Build a Memory Game in React</a>
      </p><p>Construir aplicaciones web interactivas y accesibles es una habilidad crucial para los desarrolladores modernos. React, una de las bibliotecas de JavaScript más populares, ofrece una forma poderosa de crear interfaces de usuario dinámicas. Una gran manera de profundizar tu conocimiento de React mientras trabajas en un proyecto es crear un juego de memoria - un desafío atractivo que abarca conceptos clave del desarrollo, como la gestión del estado, el diseño de componentes y las interacciones del usuario.</p><p>Acabamos de publicar un curso en el canal de YouTube freeCodeCamp.org que te enseñará a construir un juego de memoria completamente interactivo en React, con un gran énfasis en accesibilidad. Este curso, desarrollado por Scrimba, te guía a través de todo el proceso de desarrollo, desde la obtención de datos de una API y el diseño de componentes reutilizables hasta la implementación de la lógica del juego y las mejores prácticas de accesibilidad. Al final del curso, tendrás un proyecto pulido y listo para mostrar tus habilidades.</p><p>El curso empieza cubriendo los aspectos fundamentales del juego, incluyendo la obtención y almacenamiento de datos de una API, la renderización de componentes dinámicos y la garantía de interacciones fluidas para el usuario. Aprenderás a recuperar y gestionar los datos de juego, barajar y aleatorizar los elementos, así como a manejar la lógica del juego, como la detección de coincidencias y el seguimiento del progreso.</p><p>La accesibilidad es un enfoque clave de este proyecto, garantizando que el juego sea inclusivo y fácil de usar. Explorarás técnicas esenciales de accesibilidad , incluyendo el uso de atributos ARIA , HTML semántico y navegación con teclado. Además, el curso profundiza en la gestión eficiente del estado de la aplicación, el manejo adecuado de errores y la refactorización de componentes para mejorar su mantenimiento.</p><p>Algunos de los temas claves que se abordan en este curso incluyen:</p><ul><li>Obtención y almacenamiento de datos de una API utilizando el estado de React.</li><li>Renderización y gestión de componentes dinámicos del juego.</li><li>Implementación de la lógica para seleccionar, emparejar y barajar elementos del juego.</li><li>Construcción de componentes reutilizables, como un botón interactivo de emoji </li><li>Mejora de la accesibilidad mediante atributos ARIA y consideraciones para tecnologías de asistencia.</li><li>Manejo de errores y mejora de la experiencia del usuario con componentes de interfaz dedicados</li><li>Gestión y refactorización de datos de formularios dentro del estado de React </li></ul><p>Al final de este curso no solo tendrás un juego de memoria completamente funcional si no que también obtendrás un conocimiento mas profundo del desarrollo de React y sus principios sobre la accesibilidad. Ya sea que estes buscando mejorar tus habilidades en React, crear un proyecto para tu portafolio o aprender más sobre desarrollo web inclusivo, este tutorial es un recurso excelente. </p><p>¡Mira el curso completo en el canal de YouTube de <a href="https://www.youtube.com/watch?v=MzVbgZgGON4">freeCodeCamp.org</a> y comienza hoy mismo a construir tu propio juego de memoria interactivo!</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/MzVbgZgGON4?start=12412&amp;feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="Build a Memory Game in React Tutorial" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><p>Si este artículo fue de ayuda, <a href="https://x.com/intent/post?text=Build%20a%20Memory%20Game%20in%20React%0A%0Ahttps://www.freecodecamp.org/news/build-a-memory-game-in-react/">¡Compártelo!</a></p><p>Aprende a programar gratis. El plan de estudios de código abierto de freeCodeCamp ha ayudado a más de 40,000 personas a conseguir empleo como desarrolladores. <a href="https://www.freecodecamp.org/learn/">¡Empieza hoy mismo!</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Como construir una app de chat en tiempo real con ReactJS y Firebase ]]>
                </title>
                <description>
                    <![CDATA[ En este artículo, voy a mostrarte como crear una app de chat en tiempo real usando React.js y Firebase. En la app, vamos a permitir que los usuarios se logueen con su cuenta de Google usando el servicio de inicio de sesion de Firebase. También guardaremos y recuperaremos todos los ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-construir-una-app-de-chat-en-tiempo-real-con-reactjs-y-firebase/</link>
                <guid isPermaLink="false">6638d45a29a7ee03fce3bb36</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lucas Agustín Morales Romero ]]>
                </dc:creator>
                <pubDate>Tue, 15 Apr 2025 14:56:10 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/05/Cover-Images-freeCodeCamp.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/building-a-real-time-chat-app-with-reactjs-and-firebase/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Build a Real-time Chat App with ReactJS and Firebase</a>
      </p><p>En este artículo, voy a mostrarte como crear una app de chat en tiempo real usando React.js y Firebase.</p><p>En la app, vamos a permitir que los usuarios se logueen con su cuenta de Google usando el servicio de inicio de sesion de Firebase. También guardaremos y recuperaremos todos los mensajes usando el servicio Firestone de Firebase. </p><h2 id="prerrequisitos"><strong>Prerrequisitos</strong></h2><p>Para este tutorial, deberás tener instalado Node.js en tu sistema, además deberás tener conocimientos intermedios de CSS, JavaScript y ReactJS y conocimientos sobre el uso de la terminal de líneas de comandos. No debes saber utilizar Firebase para hacer el mismo.</p><h2 id="-que-es-firebase"><strong>¿Que es Firebase?</strong></h2><p>Firebase es un <a href="https://es.wikipedia.org/wiki/Backend_as_a_service">BaaS (Backend as a Service)</a>, una plataforma de backend propiedad de Google que permite a desarrolladores construir aplicaciones para iOS, Android o aplicaciones web.</p><p>Nos provee con herramientas para el seguimiento de análisis, reportar y solucionar fallas de aplicaciones y crear pruebas de marketing y productos. Esto, ayuda a los desarrolladores a crear aplicaciones de calidad, hacer crecer su base de usuarios y obtener ganancias.</p><p>Para este tutorial, vamos a utilizar dos herramientas: Firebase Authentication y Cloud Firestone.</p><h3 id="firebase-authentication"><strong><strong><strong>Firebase Authentication</strong></strong></strong></h3><p>Firebase Authentication (SDK) es una herramienta de Firebase que permite realizar autenticación de usuarios mediante varios métodos, como contraseñas, números de telefono, cuentas de Google, Facebook, Twitter, GitHub y mas. En este proyecto usaremos la autenticación a través de cuentas de Google.</p><h3 id="cloud-firestore"><strong><strong><strong>Cloud Firestore</strong></strong></strong></h3><p>Cloud Firestones es un servicio de bases de datos no relacionales que nos permite guardar y sincronizar información. Este servicio guarda la información en documentos como pares de clave-valor, y estos documentos en colecciones.</p><p>Los documentos, a su vez pueden tener sub-colecciones, permitiéndonos anidar colecciones dentro de otras colecciones, además la información se sincroniza automáticamente entre todos los dispositivos que estén consultando esos datos.</p><p>Ahora que ya tienes una idea general de como trabajan Firebase y Cloud Firestone, vamos a trabajar en el proyecto.</p><p><strong><strong>Not</strong>a<strong>:</strong></strong> para este proyecto, ya escribí el CSS y preconstruí los componentes para la aplicación de chat, puedes encontrar el código del proyecto final en <a href="https://github.com/Timonwa/react-chat">GitHub</a> y los componentes junto con el CSS en la carpeta <a href="https://github.com/Timonwa/react-chat/tree/main/setup">"setup"</a>, y también puedes ver el proyecto final funcionando en este <a href="https://react-chat-timonwa.vercel.app/">link</a></p><h2 id="como-crear-nuestra-aplicaci-n-de-react"><strong>Como crear nuestra aplicación de React</strong></h2><p>Clona este repositorio de <a href="https://github.com/Timonwa/react-chat">GitHub</a>, borra la carpeta src en el directorio raíz y remplázala con la carpeta "src" que se encuentra en la carpeta <a href="https://github.com/Timonwa/react-chat/tree/main/setup">"setup"</a>.</p><p>Alternativamente, puedes crear tu aplicación de React corriendo el comando create-react-app en la terminal.</p><pre><code class="language-node">npx create-react-app react-chat</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/06/install-create-react-app.jpg" class="kg-image" alt="install-create-react-app" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/06/install-create-react-app.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/2024/06/install-create-react-app.jpg 776w" width="776" height="430" loading="lazy"><figcaption>Comando create-react-app corriendo (en la parte superior se ve el comando de instalación: npm install -g create-react.app)</figcaption></figure><p><code>react-chat</code> &nbsp;es el nombre de la aplicación. Una vez que finaliza correremos el comando &nbsp;<code>npm install firebase react-firebase-hooks</code> para instalar <strong><strong>firebase</strong></strong> y <strong><strong>react-firebase-hooks</strong></strong>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/06/install-firebase.jpg" class="kg-image" alt="install-firebase" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/06/install-firebase.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/2024/06/install-firebase.jpg 770w" width="770" height="267" loading="lazy"><figcaption>Comando install firebase react-firebase-hooks corriendo (se aprecia que primeramente se cambio al directorio react-chat</figcaption></figure><p>Una vez que finaliza, borra la carpeta <strong><strong>src</strong></strong> y reemplazala con la que descargaste de la <a href="https://github.com/Timonwa/react-chat/tree/main/setup">carpeta setup</a> para usar el archivo CSS y los componentes preconstruidos. (Opcionalmente, puedes escribir tus propios componentes y tu propio archivo CSS)</p><p>Ahora, tu carpeta <strong><strong>src</strong></strong> contiene los siguientes componentes:</p><ul><li>Una carpeta de componentes con un componente <strong><strong>NavBar</strong></strong> que contiene los botones de <strong><strong>Google sign-in</strong></strong> y <strong><strong>Sign Out</strong>,</strong></li><li>Un componente que le da la <strong>Bienvenida</strong> al usuario que no está logueado,</li><li>Un componente <strong><strong>Chatbox</strong></strong> que solo es visible cuando el usuario está logueado.</li><li>El componente <strong><strong>Message</strong></strong> para mostrar los mensajes de los usuarios,y</li><li>El componente <strong><strong>SendMessage</strong></strong> que usaremos para que el usuario pueda escribir y enviar sus mensajes.</li></ul><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/07/componentes-en-vs-code.jpg" class="kg-image" alt="https://paper-attachments.dropboxusercontent.com/s_B6DDEC735898D3445BBF655B53FFE42B53361DCD6229DE6836CD5302F930DF9D_1667917866994_image2.png" width="146" height="217" loading="lazy"></figure><p>Además, tenemos lo siguiente:</p><ul><li>Una carpeta <strong><strong>img</strong></strong> donde se guarda la imagen de Google sign-in y sign-out,</li><li>un archivo CSS llamado <strong><strong>App.css</strong></strong> con el código CSS para la aplicación,</li><li>un archivo llamado <strong><strong>App.js</strong></strong> con todos los componentes importados dentro,</li><li>y un archivo llamado <strong><strong>index.js</strong></strong>.</li></ul><p>Corre el comando <code>npm start</code> para ver la aplicación en el navegador, deberías ver algo como esto:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/07/react-chat-running.jpg" class="kg-image" alt="https://paper-attachments.dropboxusercontent.com/s_B6DDEC735898D3445BBF655B53FFE42B53361DCD6229DE6836CD5302F930DF9D_1667916861885_image1.png" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/07/react-chat-running.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2024/07/react-chat-running.jpg 1000w, https://www.freecodecamp.org/espanol/news/content/images/size/w1600/2024/07/react-chat-running.jpg 1600w, https://www.freecodecamp.org/espanol/news/content/images/2024/07/react-chat-running.jpg 1916w" sizes="(min-width: 1200px) 1200px" width="1916" height="920" loading="lazy"><figcaption>Nuestra app de React corriendo en Local</figcaption></figure><p>Ahora, creemos una cuenta de Firebase y configuremos nuestro proyecto.</p><h2 id="como-configurar-el-proyecto-en-firebase"><strong>Como configurar el proyecto en Firebase</strong></h2><p>Si todavía no tienes una cuenta de <a href="https://firebase.google.com/">Firebase</a>, puedes abrir una usando tu correo de Gmail (solo se puede usar el <a href="https://mail.google.com/mail">mail de Google</a>). </p><p>En la página de inicio, haga clic en <strong>comenzar </strong>y luego en crear un proyecto.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-web.jpg" class="kg-image" alt="firebase-web" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/07/firebase-web.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2024/07/firebase-web.jpg 1000w, https://www.freecodecamp.org/espanol/news/content/images/size/w1600/2024/07/firebase-web.jpg 1600w, https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-web.jpg 1903w" sizes="(min-width: 1200px) 1200px" width="1903" height="924" loading="lazy"></figure><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-web-crear.jpg" class="kg-image" alt="firebase-web-crear" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/07/firebase-web-crear.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2024/07/firebase-web-crear.jpg 1000w, https://www.freecodecamp.org/espanol/news/content/images/size/w1600/2024/07/firebase-web-crear.jpg 1600w, https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-web-crear.jpg 1920w" sizes="(min-width: 1200px) 1200px" width="1920" height="918" loading="lazy"></figure><p>Completa el formulario para crear el proyecto agregando un nombre de proyecto (en nuestro caso usaremos el nombre React-chat). Si quieres dejar habilitado Google Analytics para tu proyecto déjalo, sino deshabilítalo. Después, haz clic en crear proyecto</p><p></p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-paso1.jpg" class="kg-image" alt="https://paper-attachments.dropboxusercontent.com/s_B6DDEC735898D3445BBF655B53FFE42B53361DCD6229DE6836CD5302F930DF9D_1667930368482_image5.png" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/07/firebase-paso1.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2024/07/firebase-paso1.jpg 1000w, https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-paso1.jpg 1177w" width="1177" height="739" loading="lazy"><figcaption>Creando el proyecto paso 1</figcaption></figure><p>Una vez creado, presiona el botón <strong><strong>Continu</strong>ar</strong>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-pasoFinal.jpg" class="kg-image" alt="https://paper-attachments.dropboxusercontent.com/s_B6DDEC735898D3445BBF655B53FFE42B53361DCD6229DE6836CD5302F930DF9D_1667930783189_image8.png" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/07/firebase-pasoFinal.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2024/07/firebase-pasoFinal.jpg 1000w, https://www.freecodecamp.org/espanol/news/content/images/size/w1600/2024/07/firebase-pasoFinal.jpg 1600w, https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-pasoFinal.jpg 1832w" sizes="(min-width: 1200px) 1200px" width="1832" height="906" loading="lazy"><figcaption>Lo que verás mientras Firebase crea tu proyecto.</figcaption></figure><p>Elige el tipo de aplicación al que quieres agregar Firebase, para este artículo elegiremos el icono de código porque estamos desarrollando una aplicación web. </p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-final-1.jpg" class="kg-image" alt="firebase-final-1" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/07/firebase-final-1.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2024/07/firebase-final-1.jpg 1000w, https://www.freecodecamp.org/espanol/news/content/images/size/w1600/2024/07/firebase-final-1.jpg 1600w, https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-final-1.jpg 1920w" sizes="(min-width: 1200px) 1200px" width="1920" height="913" loading="lazy"><figcaption>Elige el tipo de aplicación al que quieres integrar Firebase</figcaption></figure><p></p><p>Ingresa un sobrenombre para la aplicación y haz clic en el botón <strong>registrar app</strong>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-registrar-app.jpg" class="kg-image" alt="https://paper-attachments.dropboxusercontent.com/s_B6DDEC735898D3445BBF655B53FFE42B53361DCD6229DE6836CD5302F930DF9D_1667934485512_image10.png" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/07/firebase-registrar-app.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2024/07/firebase-registrar-app.jpg 1000w, https://www.freecodecamp.org/espanol/news/content/images/size/w1600/2024/07/firebase-registrar-app.jpg 1600w, https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-registrar-app.jpg 1916w" sizes="(min-width: 1200px) 1200px" width="1916" height="916" loading="lazy"><figcaption>Registra tu app</figcaption></figure><p>En el menú selecciona npm, copia el código (lo vamos a utilizar más tarde) y haz clic en <strong>ir a la consola</strong>. </p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-sdk.jpg" class="kg-image" alt="https://paper-attachments.dropboxusercontent.com/s_B6DDEC735898D3445BBF655B53FFE42B53361DCD6229DE6836CD5302F930DF9D_1667934988668_image11.png" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/07/firebase-sdk.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2024/07/firebase-sdk.jpg 1000w, https://www.freecodecamp.org/espanol/news/content/images/size/w1600/2024/07/firebase-sdk.jpg 1600w, https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-sdk.jpg 1920w" sizes="(min-width: 1200px) 1200px" width="1920" height="918" loading="lazy"><figcaption>Código para el SDK de Firebase</figcaption></figure><h2 id="c-mo-configurar-firebase-authentication"><strong>Cómo configurar Firebase Authentication</strong></h2><p>Para configurar Firebase Authentication, ve al menú de la izquierda y presiona sobre <strong>Compilación </strong>y selecciona <strong>Aut<strong>hentication</strong></strong> del desplegable.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-autentication.jpg" class="kg-image" alt="https://paper-attachments.dropboxusercontent.com/s_B6DDEC735898D3445BBF655B53FFE42B53361DCD6229DE6836CD5302F930DF9D_1667937032772_image12.png" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/07/firebase-autentication.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2024/07/firebase-autentication.jpg 1000w, https://www.freecodecamp.org/espanol/news/content/images/size/w1600/2024/07/firebase-autentication.jpg 1600w, https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-autentication.jpg 1920w" sizes="(min-width: 1200px) 1200px" width="1920" height="918" loading="lazy"><figcaption>Selecciona Authentication del menú desplegable.</figcaption></figure><p>Haz clic en comenzar y selecciona a Google en el menú proveedores de acceso.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-autentication-conf.jpg" class="kg-image" alt="https://paper-attachments.dropboxusercontent.com/s_B6DDEC735898D3445BBF655B53FFE42B53361DCD6229DE6836CD5302F930DF9D_1667937105063_image13.png" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/07/firebase-autentication-conf.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2024/07/firebase-autentication-conf.jpg 1000w, https://www.freecodecamp.org/espanol/news/content/images/size/w1600/2024/07/firebase-autentication-conf.jpg 1600w, https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-autentication-conf.jpg 1920w" sizes="(min-width: 1200px) 1200px" width="1920" height="920" loading="lazy"><figcaption>Elige el método de autenticación.</figcaption></figure><p>Presiona sobre el botón de la derecha para habilitar el proyecto y selecciona un correo de soporte, luego haz clic en guardar.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-autentication-conf-mail.jpg" class="kg-image" alt="https://paper-attachments.dropboxusercontent.com/s_B6DDEC735898D3445BBF655B53FFE42B53361DCD6229DE6836CD5302F930DF9D_1667937310260_image14.png" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/07/firebase-autentication-conf-mail.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2024/07/firebase-autentication-conf-mail.jpg 1000w, https://www.freecodecamp.org/espanol/news/content/images/size/w1600/2024/07/firebase-autentication-conf-mail.jpg 1600w, https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-autentication-conf-mail.jpg 1920w" sizes="(min-width: 1200px) 1200px" width="1920" height="921" loading="lazy"></figure><h2 id="c-mo-configurar-cloud-firestone"><strong>Cómo configurar Cloud Firestone</strong></h2><p>Nuevamente. ve al menú de la izquierda, clickea en <strong>compilación</strong> y selecciona <strong>Firestone</strong>. Luego haz clic en crear base de datos y completa el formulario.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/07/firestone-db.jpg" class="kg-image" alt="image9-2" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/07/firestone-db.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2024/07/firestone-db.jpg 1000w, https://www.freecodecamp.org/espanol/news/content/images/size/w1600/2024/07/firestone-db.jpg 1600w, https://www.freecodecamp.org/espanol/news/content/images/2024/07/firestone-db.jpg 1920w" sizes="(min-width: 1200px) 1200px" width="1920" height="921" loading="lazy"><figcaption>Configurando Cloud Firestore</figcaption></figure><p>En el primer paso, selecciona la localización de la base de datos, por defecto el sistema asignará la ubicación más cercana a la que te encuentras, luego presiona siguiente. </p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/07/firestone-db-seteo1.jpg" class="kg-image" alt="firestone-db-seteo1" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/07/firestone-db-seteo1.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2024/07/firestone-db-seteo1.jpg 1000w, https://www.freecodecamp.org/espanol/news/content/images/size/w1600/2024/07/firestone-db-seteo1.jpg 1600w, https://www.freecodecamp.org/espanol/news/content/images/2024/07/firestone-db-seteo1.jpg 1920w" sizes="(min-width: 1200px) 1200px" width="1920" height="916" loading="lazy"><figcaption>Setea la localización de la base de datos y presiona siguiente.</figcaption></figure><p>Selecciona el modo de la base de datos, puedes elegir producción o modo de prueba.</p><p>El modo de prueba permite que cualquier cliente puede leer o escribir en la base de datos por 30 días. El modo producción significa que nadie puede leer o escribir en la base de datos, por lo que debes escribir tus reglas para brindar acceso a la base de datos.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/07/firestone-db-seteo2.jpg" class="kg-image" alt="image10-2" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/07/firestone-db-seteo2.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2024/07/firestone-db-seteo2.jpg 1000w, https://www.freecodecamp.org/espanol/news/content/images/size/w1600/2024/07/firestone-db-seteo2.jpg 1600w, https://www.freecodecamp.org/espanol/news/content/images/2024/07/firestone-db-seteo2.jpg 1920w" sizes="(min-width: 1200px) 1200px" width="1920" height="920" loading="lazy"><figcaption>Elige el modo de producción o el modo de pruebas (nosotros elegiremos el modo de producción).</figcaption></figure><p>Selecciona <strong>modo de producción</strong> y presiona el botón <strong>crear</strong>.</p><p>El siguiente paso será editar nuestras reglas, haz clic en la pestaña <strong>reglas</strong>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-reglas-1.jpg" class="kg-image" alt="image12-2" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/07/firebase-reglas-1.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2024/07/firebase-reglas-1.jpg 1000w, https://www.freecodecamp.org/espanol/news/content/images/size/w1600/2024/07/firebase-reglas-1.jpg 1600w, https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-reglas-1.jpg 1920w" sizes="(min-width: 1200px) 1200px" width="1920" height="899" loading="lazy"><figcaption>Edita las reglas ingresando en la pestaña Reglas.</figcaption></figure><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-reglas-2.jpg" class="kg-image" alt="image13-2" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/07/firebase-reglas-2.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2024/07/firebase-reglas-2.jpg 1000w, https://www.freecodecamp.org/espanol/news/content/images/size/w1600/2024/07/firebase-reglas-2.jpg 1600w, https://www.freecodecamp.org/espanol/news/content/images/2024/07/firebase-reglas-2.jpg 1920w" sizes="(min-width: 1200px) 1200px" width="1920" height="918" loading="lazy"><figcaption>Reemplaza esta regla.</figcaption></figure><p>Reemplaza la regla que esta en la pestaña por la siguiente y presiona el boton <strong>publicar</strong>.</p><pre><code class="language-js">rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read: if true;
      allow create, update, delete, write: if request.auth != null;
    }
  }
}</code></pre><p>Analicemos el código, la primer parte, &nbsp;<code>allow read: if true;</code> significa que cualquiera puede leer tu base de datos, mientras que el bloque de código siguiente <code>allow create, update, delete, write: if request.auth != null;</code> significa que solo los clientes autenticados pueden crear, actualizar, borrar y escribir datos en la base de datos.</p><p>Puedes comenzar agregando o creando una colección en tu base de datos o crear una automáticamente en la aplicación que estamos desarrollando, lo que haremos después. Si quieres crear una colección en Cloud Firestone, vuelve a la pestaña de <strong>datos </strong>presionando en la misma y presiona el botón <strong>iniciar coleccion</strong>.</p><p>Ingresa el nombre de la colección, por ejemplo "messages" y clickea en <strong>siguiente</strong>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/07/coleecion-ejemplo.jpg" class="kg-image" alt="image14-2" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/07/coleecion-ejemplo.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2024/07/coleecion-ejemplo.jpg 1000w, https://www.freecodecamp.org/espanol/news/content/images/2024/07/coleecion-ejemplo.jpg 1099w" width="1099" height="626" loading="lazy"><figcaption>Iniciando una colección en Firestone.</figcaption></figure><p>Ahora, crea el documento para la colección, </p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/07/coleecion-ejemplo-2.jpg" class="kg-image" alt="image15-1" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2024/07/coleecion-ejemplo-2.jpg 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2024/07/coleecion-ejemplo-2.jpg 1000w, https://www.freecodecamp.org/espanol/news/content/images/2024/07/coleecion-ejemplo-2.jpg 1117w" width="1117" height="664" loading="lazy"><figcaption>Creando un documento para la colección.</figcaption></figure><p>Clickea en <strong>ID automático</strong>, de esa forma el sistema generará el ID de forma automática, o si lo prefieres ingresa uno tu.</p><p>Luego, se crea el par clave-&gt;valor para el documento. El campo llamado "Campo" representa el nombre de la clave, el campo "Tipo" nos dice que valor almacena (cadena de texto, number, timeStamp, etc), y finalmente el campo "Valor" es lo que guardamos en esta clave.</p><p>Puedes seguir agregando más documentos presionando en el enlace "Agregar campo", sino presiona en Guardar para que se guarde la colección.</p><p>Nuestro proyecto de Firebase ya esta correctamente seteado, volvamos a la aplicación de React.</p><h2 id="c-mo-configurar-firebase-en-react"><strong>Cómo configurar Firebase en React</strong></h2><p>En la carpeta <strong><strong>src</strong></strong>, crea un archivo llamado <code>firebase.js</code> y pega el código que copiamos al configurar firebase.</p><p>Tambien importemos los servicios <code>getAuth</code> &nbsp;y <code>getFirestore</code> desde <a href="https://firebase.google.com/docs/auth/web/start?hl=es">Autenticación para Web</a> y <a href="https://firebase.google.com/docs/firestore/quickstart?hl=es">Firestore en la nube para web</a> respectivamente. Puedes leer más sobre las bibliotecas disponibles para Firebase en la página de la <a href="https://firebase.google.com/docs/web/setup?hl=es#available-libraries">documentación</a>.</p><p>Nuestro archivo firebase.js debe verse asi:</p><pre><code class="language-js">// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
  apiKey: LA_CLAVE_PARA_LA_APLICACION_VA_AQUI(CLAVE_API),
  authDomain: EL_DOMINIO_DE_AUTENTICACION_PARA_LA_APP_VA_AQUI,
  projectId: EL_ID_DEL_PROYECTO_PARA_LA_APP_VA_AQUI,
  storageBucket: EL_BUCKET_PARA_LA_APLICACION_VA_AQUI,
  messagingSenderId: EL_ID_PARA_ENVIAR_LOS_MENSAJES_VA_AQUI,
  appId: EL_APP_ID_DE_LA_APLICACION_DE_REACT_VA_AQUI,
};</code></pre><h2 id="c-mo-implementar-firebase-dentro-de-nuestra-aplicaci-n-react"><strong>Cómo implementar Firebase dentro de nuestra aplicación React</strong></h2><h3 id="c-mo-autenticar-usuarios-con-su-cuenta-de-google"><strong>Cómo autenticar usuarios con su cuenta de Google</strong></h3><p>Nosotros queremos que los usuarios tengan acceso a la aplicación de chat y que en caso de que estén autenticados puedan enviar mensajes, si no lo están queremos que vean la página de bienvenida para poder loguearse usando el botón de Google sign-in, y si esta logueado debe poder ver el botón de Sign-out para salir del chat.</p><p>Esta autenticación será controlada por el componente NavBar, que contiene ambos botones (sign-in y sign-out).</p><p>En nuestro componente <code>NavBar</code>, importaremos nuestra imagen de Google "sign-in", y la guardaremos como la constante llamada <code>GoogleSignin</code>. También tendremos un estado llamado <code>user</code>, seteado como falso, una función llamada <code>googleSignIn</code> que setea el estado de <code>user</code> como verdadero, y una función llamada <code>signOut</code> que setea el estado de <code>user</code> como falso. </p><p>También tenemos un elemento <code>nav</code> con un tag <code>h1</code> que representa el título de nuestra app y dos botones que se renderizan condicionalmente en base al estado del componente <code>user</code></p><pre><code class="language-js">import React, { useState } from "react";
import GoogleSignin from "../img/btn_google_signin_dark_pressed_web.png";

const NavBar = () =&gt; {
  const [user, setUser] = useState(false);
  const googleSignIn = () =&gt; {
    setUser(true);
  };
  const signOut = () =&gt; {
    setUser(false);
  };
  return (
    &lt;nav className="nav-bar"&gt;
      &lt;h1&gt;React Chat&lt;/h1&gt;
      {user ? (
        &lt;button onClick={signOut} className="sign-out" type="button"&gt;
          Sign Out
        &lt;/button&gt;
      ) : (
        &lt;button className="sign-in"&gt;
          &lt;img
            onClick={googleSignIn}
            src={GoogleSignin}
            alt="sign in with google"
            type="button"
          /&gt;
        &lt;/button&gt;
      )}
    &lt;/nav&gt;
  );
};
export default NavBar;</code></pre><p>Hagamos un cambio en el componente <strong>NavBar,</strong> importando lo siguiente:</p><pre><code class="language-js">import { auth } from "../firebase";
import { useAuthState } from "react-firebase-hooks/auth";
import { GoogleAuthProvider, signInWithRedirect } from "firebase/auth";</code></pre><p>Reemplaza el estado del usuario con el siguiente código:</p><p><code>const [user] = useAuthState(auth);</code></p><p>Y edita las funciones googleSignIn y signOut:</p><pre><code class="language-js">const googleSignIn = () =&gt; {
  const provider = new GoogleAuthProvider();
  signInWithRedirect(auth, provider);
};
const signOut = () =&gt; {
  auth.signOut();
};</code></pre><p>La función <code>useAuthState</code> se dispara en el momento que el usuario se loguea o desloguea, permitiéndonos acceder a los detalles del usuario. Actualmente, el estado del usuario es <code>null</code>, una vez que te logueas, el estado del usuario cambiará a la información suministrada por el método de autenticación (en este caso, Google).</p><p>En la función <code>googleSignIn</code>, le indicamos a Firebase que el el usuario quiere autenticarse con Google usando el <code>GoogleAuthProvider()</code>. También lo redirige a la página de inicio de sesión de Google.</p><p>Luego de el correcto inicio de sesión del usuario, sus datos son almacenados en <code>auth</code>, y el usuario es redireccionado a la app. La función <code>signOut</code> borra la información almacenada de autenticación, retornando ésta a <code>null</code>. El nuevo estado almacenado también determina qué botón debe renderizarse.</p><p>Agreguemos también autenticación a nuestro archivo <strong>App.js</strong>. Importa lo siguiente:</p><pre><code class="language-js">import { auth } from "./firebase";
import { useAuthState } from "react-firebase-hooks/auth";</code></pre><p>Agrega el nuevo user state, para que podamos renderizar el componente <strong>Welcome</strong> si el usuario no está logueado o el componente <strong>Chatbox</strong> si el usuario ya se autenticó.</p><pre><code class="language-js">const [user] = useAuthState(auth);</code></pre><p>El codigo final debe lucir así:</p><pre><code class="language-js">import { auth } from "./firebase";
import { useAuthState } from "react-firebase-hooks/auth";
import "./App.css";
import NavBar from "./components/NavBar";
import ChatBox from "./components/ChatBox";
import Welcome from "./components/Welcome";

function App() {
  const [user] = useAuthState(auth);
  return (
    &lt;div className="App"&gt;
      &lt;NavBar /&gt;
      {!user ? &lt;Welcome /&gt; : &lt;ChatBox /&gt;}
    &lt;/div&gt;
  );
}
export default App;</code></pre><p>Al probar nuestras nuevas funciones de sign-in y sign-out, veremos lo siguiente:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/01/ezgif-4-b6465d1647-1.gif" class="kg-image" alt="ezgif-4-b6465d1647-1" width="600" height="400" loading="lazy"><figcaption>App demo</figcaption></figure><p>Ahora, hagamos lo mismo con el componente <strong>Welcome, </strong>que ahora tiene el siguiente código:</p><pre><code class="language-js">import React from "react";
import GoogleSignin from "../img/btn_google_signin_dark_pressed_web.png";

const Welcome = () =&gt; {
  const googleSignIn = () =&gt; {};

  return (
    &lt;main className="welcome"&gt;
      &lt;h2&gt;Welcome to React Chat.&lt;/h2&gt;
      &lt;img src="/logo512.png" alt="ReactJs logo" width={50} height={50} /&gt;
      &lt;p&gt;Sign in with Google to chat with with your fellow React Developers.&lt;/p&gt;
      &lt;button className="sign-in"&gt;
        &lt;img
          onClick={googleSignIn}
          src={GoogleSignin}
          alt="sign in with google"
          type="button"
        /&gt;
      &lt;/button&gt;
    &lt;/main&gt;
  );
};
export default Welcome;</code></pre><p>Importaremos lo siguiente:</p><pre><code class="language-js">import { auth } from "../firebase";
import { GoogleAuthProvider, signInWithRedirect } from "firebase/auth";</code></pre><p>y tambien editaremos la funcion googleSignIn:</p><pre><code class="language-js">const googleSignIn = () =&gt; {
    const provider = new GoogleAuthProvider();
    signInWithRedirect(auth, provider);
};</code></pre><p>Ahora, podremos loguearnos desde el botón que se renderiza en el componente <strong>Welcome</strong>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/01/video2-1.gif" class="kg-image" alt="video2-1" width="600" height="400" loading="lazy"><figcaption>Updated app demo</figcaption></figure><h3 id="como-enviar-y-almacenar-mensajes-en-firebase-"><strong>Como enviar y almacenar mensajes en Firebase:</strong></h3><p>Actualmente, estamos mostrando un mensaje de prueba de nuestro componente <strong>Message y, </strong>el botón<strong> <strong>Send</strong></strong> no realiza ninguna acción. Cuando ingresamos un mensaje y pulsamos el botón <strong><strong>Send</strong></strong>, queremos que el mensaje se envíe de inmediato a la aplicación.</p><p>Así, que editemos el componente <strong><strong>SendMessage</strong>:</strong> </p><p>Primero, importamos <code>useState</code> de React, <code>auth</code> y <code>db</code> &nbsp;de nuestro archivo de configuración de firebase, y <code>addDoc</code>, <code>collection</code> y <code>serverTimestamp</code> de la librería Firestore.</p><pre><code class="language-js">import React, { useState } from "react";
import { auth, db } from "../firebase";
import { addDoc, collection, serverTimestamp } from "firebase/firestore";</code></pre><p>Crearemos un estado llamado <code>message</code> que inicialmente esté definido como una cadena vacía y se pase como valor a la etiqueta <code>input</code>. La funcion <code>onChange</code> es también agregada a el input, que setea el estado del <code>message</code> en lo que el usuario tipee.</p><pre><code class="language-js">const SendMessage = () =&gt; {
  const [message, setMessage] = useState("");
    
  return (
    &lt;form className="send-message"&gt;
      &lt;input
        ...
        value={message}
        onChange={(e) =&gt; setMessage(e.target.value)}
      /&gt;
      &lt;button type="submit"&gt;Send&lt;/button&gt;
    &lt;/form&gt;
  );
};</code></pre><p>También creamos una función llamada <code>sendMessage</code>, y agregamos el atributo <code>onSubmit</code> en nuestro formulario, que ejecuta la función <code>sendMessage</code> cuando el usuario clickea en el botón <code>Send</code> (enviar). Nótese que el botón debe tener el tipo <code>type="submit"</code> &nbsp;para que la función funcione.</p><pre><code class="language-js"> const sendMessage = async (event) =&gt; {
    event.preventDefault();
    if (message.trim() === "") {
      alert("Enter valid message");
      return;
    }
    const { uid, displayName, photoURL } = auth.currentUser;
    await addDoc(collection(db, "messages"), {
      text: message,
      name: displayName,
      avatar: photoURL,
      createdAt: serverTimestamp(),
      uid,
    });
    setMessage("");
  };
  
  return (
    &lt;form onSubmit={(event) =&gt; sendMessage(event)} className="send-message"&gt;
...</code></pre><p>La función <code>sendMessage</code> es una funcion asincrona. Primero chequea si el el usuario esta intentando enviar una cadena vacía o espacios vacíos como un mensaje y, en caso de que así sea alerta al usuario. </p><p>Si el mensaje no es una cadena vacía, toma del usuario los siguientes datos dela información proporcionada por <code>auth</code> al logguearse: <strong>uid, displayName </strong>y <strong>photoURL.</strong> Estos datos corresponden con el identificador único del usuario, su nombre y la URL de la foto del usuario, respectivamente.</p><p>Luego, usa la función <code>addDoc()</code> para crear un <strong>documento</strong> dentro de la <strong>colección</strong> <strong>messages</strong> de nuestra <strong>base de datos, </strong>a la que accede gracias al archivo que importamos. En caso de que la <strong>colección</strong> no exista, crea una por nosotros. </p><p>También crea un par de <strong>llave-valor</strong>, guardando nuestro <strong>mensaje</strong> en <strong>text, displayName </strong>en <strong>name, </strong>guardando el momento en el que el mensaje fue guardado en nuestra base de datos en la columna <strong>createAt</strong>, y el <strong>uid</strong>.</p><p>Estos pares de llave-valor, conforman los datos de nuestro documento. Una vez que se completa la acción, se restablece el estado del mensajea una cadena vacía.</p><h3 id="como-recuperar-mensajes-de-nuestra-base-de-datos"><strong>Como recuperar mensajes de nuestra base de datos</strong></h3><p>Luego de enviar el mensaje del usuario, debemos mostrarlo en la pantalla. Vayamos a nuestro componente <strong>chatBox</strong> e importemos lo siguiente:</p><pre><code class="language-js">import { useEffect, useRef, useState } from "react";
import {
  query,
  collection,
  orderBy,
  onSnapshot,
  limit,
} from "firebase/firestore";
import { db } from "../firebase";</code></pre><p>Creamos el hook <code>useEffect</code> que correra cada vez que se realizan cambios en el thatroom, por ejemplo enviar o borrar un mensaje.</p><pre><code class="language-js">useEffect(() =&gt; {
  const q = query(
    collection(db, "messages"),
    orderBy("createdAt", "desc"),
    limit(50)
  );
  const unsubscribe = onSnapshot(q, (QuerySnapshot) =&gt; {
    const fetchedMessages = [];
    QuerySnapshot.forEach((doc) =&gt; {
      fetchedMessages.push({ ...doc.data(), id: doc.id });
    });
    const sortedMessages = fetchedMessages.sort(
      (a, b) =&gt; a.createdAt - b.createdAt
    );
    setMessages(sortedMessages);
  });
  return () =&gt; unsubscribe;
}, []);</code></pre><p>En el hook <code>useEffect</code>, tenemos la constante <code>q</code>, una consulta de Firebase que busca los mensajes dentro de la base de la colección <strong>messages</strong> de nuestra base de datos, luego ordena los documentos obtenidos basados en la llave <strong>createAt </strong>y devuelve un máximo de <strong>50</strong> documentos (mensajes guardados)</p><p>La constante <code>unsubscribe</code> representa la función <code>onSnapshot</code>, que escucha los cambios en el documento, esta función tiene un arreglo vacío llamado <code>messages</code>.</p><p>El bucle <code>forEach</code> itera entre todos los <strong>documentos</strong> de la <strong>colección</strong> y guarda la información en un nuevo arreglo. Luego, asigna el arreglo inicial de mensajes al nuevo arreglo de mensajes.</p><p>También usamos el <strong>método map</strong> en nuestro arreglo de mensajes para &nbsp;cada mensaje/documento en nuestro componente <strong>message</strong>.</p><pre><code class="language-js">{messages?.map((message) =&gt; (
  &lt;Message key={message.id} message={message} /&gt;
))}</code></pre><p>El codigo completo luce asi:</p><pre><code class="language-js">import React, { useEffect, useRef, useState } from "react";
import {
  query,
  collection,
  orderBy,
  onSnapshot,
  limit,
} from "firebase/firestore";
import { db } from "../firebase";
import Message from "./Message";
import SendMessage from "./SendMessage";

const ChatBox = () =&gt; {
  const [messages, setMessages] = useState([]);
  const scroll = useRef();

  useEffect(() =&gt; {
    const q = query(
      collection(db, "messages"),
      orderBy("createdAt", "desc"),
      limit(50)
    );

    const unsubscribe = onSnapshot(q, (QuerySnapshot) =&gt; {
      const fetchedMessages = [];
      QuerySnapshot.forEach((doc) =&gt; {
        fetchedMessages.push({ ...doc.data(), id: doc.id });
      });
      const sortedMessages = fetchedMessages.sort(
        (a, b) =&gt; a.createdAt - b.createdAt
      );
      setMessages(sortedMessages);
    });
    return () =&gt; unsubscribe;
  }, []);

  return (
    &lt;main className="chat-box"&gt;
      &lt;div className="messages-wrapper"&gt;
        {messages?.map((message) =&gt; (
          &lt;Message key={message.id} message={message} /&gt;
        ))}
      &lt;/div&gt;
      {/* when a new message enters the chat, the screen scrolls down to the scroll div */}
      &lt;span ref={scroll}&gt;&lt;/span&gt;
      &lt;SendMessage scroll={scroll} /&gt;
    &lt;/main&gt;
  );
};

export default ChatBox;</code></pre><p>Vayamos a nuestro componente <strong>message</strong>, y rendericemos los datos recibidos en el navegador.</p><pre><code class="language-js">import React from "react";
import { auth } from "../firebase";
import { useAuthState } from "react-firebase-hooks/auth";
const Message = ({ message }) =&gt; {
  const [user] = useAuthState(auth);

  return (
    &lt;div
      className={`chat-bubble ${message.uid === user.uid ? "right" : ""}`}&gt;
      &lt;img
        className="chat-bubble__left"
        src={message.avatar}
        alt="user avatar"
      /&gt;
      &lt;div className="chat-bubble__right"&gt;
        &lt;p className="user-name"&gt;{message.name}&lt;/p&gt;
        &lt;p className="user-message"&gt;{message.text}&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};
export default Message;
</code></pre><p>Importamos <code>auth</code> y <code>useAuthState</code>, y almacenamos los datos del usuario en &nbsp;<code>user</code>. Deconstruimos el <code>prop</code> <strong>message</strong> y asignamos el avatar al atributo <code>src</code> del componente img. También reemplazamos el nombre y el mensaje "de prueba" con el mensaje obtenido desde <strong>message</strong>. </p><p>También aplicamos una condición al estilo CSS según el <code>uid</code> del autor del mensaje, entonces si el <code>uid</code> del autor del mensaje es el mismo que el del usuario logueado, se aplica al div el estilo definido en el selector <strong>derecho</strong>; en caso contrario no se agrega ningún estilo adicional.</p><p>Actualmente, todos &nbsp;los mensajes se posicionan a la izquierda, así que si el autor del mensaje es el usuario logueado, sus mensajes se posicionaran a la derecha. Veamos esa dinámica en acción en el navegador:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/01/video3-1.gif" class="kg-image" alt="video3-1" width="600" height="400" loading="lazy"><figcaption>Demostración de la app actualizada</figcaption></figure><p>El mensaje es enviado y almacenado en nuestra base de datos, luego todos los mensajes se recuperan y la sala es actualizada en tiempo real con los mensajes nuevos.</p><p>El nombre y el avatar de cada usuario son &nbsp;mostrados en cada tarjeta, pero también podemos ver que el chat no se desliza automáticamente hacia abajo cuando llega un nuevo mensaje. Vamos a arreglar eso.</p><h3 id="como-hacer-que-el-chat-se-desplace-hasta-el-final"><strong>Como hacer que el chat se desplace hasta el final</strong></h3><p>Vayamos al archivo <strong>ChatBox.js</strong>, importemos el hook <code>useRef</code> y creemos una constante llamada scroll:</p><pre><code class="language-js">import React, { useEffect, useRef, useState } from "react";
...
const scroll = useRef();</code></pre><p>Crearemos un elemento <code>span</code> con un atributo <code>ref</code> conectado al valor de <strong>scroll</strong>,y , también pasamos el &nbsp;<strong>s<strong>crol</strong>l </strong>en nuestro componente<strong> message.</strong></p><p>Luego, creamos un elemento <code>span</code> con un atributo <code>ref</code> cuyo valor es <strong>scroll</strong>, y también pasamos <strong>scroll</strong> a nuestro componente <code>SendMessage</code>:</p><pre><code class="language-js">&lt;main className="chat-box"&gt;
   ...
   {/* when a new message enters the chat, the screen scrolls dowwn to the scroll div */}
   &lt;span ref={scroll}&gt;&lt;/span&gt;
   &lt;SendMessage scroll={scroll} /&gt;
&lt;/main&gt;</code></pre><p>Luego vamos al componente <strong>Messages</strong>, accedemos a la constante scroll, y agregamos <code>scroll.current.scrollIntoView({ behavior: "smooth" })</code> al final de nuestra función <code>sendMessage</code>.</p><p>Este código le indica al navegador que el scroll span debe ser visible después de enviar un mensaje. Por eso ponemos la etiqueta <code>span</code> al final de cada mensaje.</p><pre><code class="language-js">const SendMessage = ({ scroll }) =&gt; {

  const sendMessage = async (event) =&gt; {
   ...
    setMessage("");
    scroll.current.scrollIntoView({ behavior: "smooth" });
  };
  ...
};</code></pre><p>Volviendo al navegador, deberíamos ver que el chat se desplaza automáticamente hasta el final cuando del usuario envía un mensaje.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/01/video4-1.gif" class="kg-image" alt="video4-1" width="600" height="400" loading="lazy"><figcaption>Demo mostrando como el chat se desliza hasta el final con un nuevo mensaje.</figcaption></figure><h2 id="como-agregar-dominios-autorizados"><strong>Como agregar dominios autorizados</strong></h2><p>Cuando desplegamos nuestra aplicacion en React, es indispensable agregar nuestro dominio a la lista de dominios autorizados en Firebase. Este paso, asegura que nuestra app se comunique correctamente con los servicios de Firebase, veamos como hacerlo:</p><p>En la consola de Firebase, navega hasta la sección <strong>Autenticación </strong>y haz click en la pestaña de <strong>configuración.</strong> Deslízate hasta la parte de <strong>Dominios autorizados; </strong>a continuación clickea en el botón<strong> agregar un dominio. </strong>Luego agrega el o los dominios donde la aplicación será desplegada. </p><p>Por ejemplo, si desplegaras la aplicación en la dirección <a href="https://my-react-chat-app.com/" rel="noreferrer nofollow noopener">https://my-react-chat-app.com</a>, ingresa <code>my-react-chat-app.com</code> como un dominio autorizado, y clickea el boton <strong>agregar</strong> para guardar los cambios.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2025/03/firestone-agregar-dominio-1.png" class="kg-image" alt="firestone-agregar-dominio-1" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2025/03/firestone-agregar-dominio-1.png 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2025/03/firestone-agregar-dominio-1.png 1000w, https://www.freecodecamp.org/espanol/news/content/images/2025/03/firestone-agregar-dominio-1.png 1366w" sizes="(min-width: 720px) 720px" width="1366" height="611" loading="lazy"></figure><p>Agregando el dominio donde desplegaras tu app a la lista de dominios autorizados, le brindas permisos a los servicios de Firestone para ser accedidos desde ese dominio. Si no hiciéramos esto, encontraríamos errores al intentar establecer conexión o realizar operaciones con Firebase.</p><h2 id="para-terminar"><strong>Para terminar</strong></h2><p>Y, así es como llegamos al final de la construcción de esta aplicación de chat en tiempo real, ¡felicitaciones!.</p><p>En este tutorial, aprendimos a usar Firebase y React para crear una aplicacion de &nbsp;chat en tiempo real. También autenticamos a los usuarios usando el método de logueo por cuenta de Google de Firetone y, guardamos los mensajes del chat usando Cloud Firestore. Finalmente, aprendimos a usar algunos servicios y librerías de Firestone.</p><p>Puedes encontrar el código de este proyecto en <a href="https://github.com/Timonwa/react-chat">GitHub</a> y puedes probar la aplicación desplegada usando el siguiente <a href="https://react-chat-timonwa.vercel.app/">enlace</a>.</p><p>Si disfrutaste este articulo, por favor compártelo para ayudar a otros desarrolladores, también puedes visitar mi <a href="https://blog.timonwa.com/">blog</a> para leer mas artículos o puedes seguirme en &nbsp;<a href="https://twitter.com/timonwa_">Twitter</a> o <a href="https://www.linkedin.com/in/timonwa/">LinkedIn</a>.</p><p>Hasta la próxima, byeeeeee!</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2025/03/HoDL1vbXj-1-1-.gif" class="kg-image" alt="HoDL1vbXj-1-1-" width="498" height="252" loading="lazy"></figure> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Error: fallo al enviar algunas referencias – Cómo solucionarlo en Git ]]>
                </title>
                <description>
                    <![CDATA[ Al colaborar con otros desarrolladores utilizando Git, es posible que te encuentres con el error error: failed to push some refs to [repositorio remoto]. Este error ocurre principalmente cuando intentas enviar tus cambios locales a GitHub, pero el repositorio local aún no está actualizado con los cambios realizados en el ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/error-fallo-al-enviar-algunas-referencias-como-solucionarlo-en-git/</link>
                <guid isPermaLink="false">67fe72756d1e700499f40228</guid>
                
                    <category>
                        <![CDATA[ Git ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Eduardo Macias ]]>
                </dc:creator>
                <pubDate>Tue, 15 Apr 2025 14:53:41 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2025/04/roman-synkevych-wX2L8L-fGeA-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/error-failed-to-push-some-refs-to-how-to-fix-in-git/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Error: failed to push some refs to – How to Fix in Git</a>
      </p><p>Al colaborar con otros desarrolladores utilizando Git, es posible que te encuentres con el error <code>error: failed to push some refs to [repositorio remoto]</code>.</p><p>Este error ocurre principalmente cuando intentas enviar tus cambios locales a GitHub, pero el repositorio local aún no está actualizado con los cambios realizados en el repositorio remoto.</p><p>En esencia, Git te está indicando que actualices el repositorio local con los cambios actuales del repositorio remoto antes de enviar tus propios cambios. Esto es necesario para evitar sobrescribir los cambios realizados por otros.</p><p>A continuación, discutiremos dos posibles formas de solucionar este error.</p><h2 id="c-mo-solucionar-el-error-error-failed-to-push-some-refs-to-en-git">Cómo solucionar el error <code>error: failed to push some refs to</code> en Git</h2><p>Podemos solucionar el error <code>error: failed to push some refs to [repositorio remoto]</code> en Git utilizando los comandos <code>git pull origin [rama]</code> o <code>git pull --rebase origin [rama]</code>. En la mayoría de los casos, la segunda opción ( <code>git pull --rebase origin [rama]</code>) resuelve el error.</p><p>Veamos cómo puedes usar los comandos mencionados.</p><h3 id="c-mo-solucionar-el-error-error-failed-to-push-some-refs-to-en-git-usando-git-pull">Cómo solucionar el error <code>error: failed to push some refs to</code> en Git usando <code>git pull</code></h3><p>Ejecutar <code>git pull</code> implica obtener los nuevos cambios realizados en el repositorio remoto y fusionarlos con el repositorio local.</p><p>Una vez que se haya realizado la fusión, podrás enviar tus propios cambios de código a GitHub.</p><p>En este caso, estamos intentando solucionar el error <code>error: failed to push some refs to [repositorio remoto]</code> ejecutando <code>git pull</code>.</p><p>Aquí te mostramos cómo hacerlo:</p><pre><code>git pull origin main
</code></pre><p>Si estás trabajando en una rama diferente, deberás reemplazar <code>main</code> en el ejemplo anterior con el nombre de tu rama.</p><p>Ten en cuenta que existe la posibilidad de que este comando no logre sincronizar tus repositorios remoto y local, y por lo tanto, no resuelva el error. Si la solicitud se completa correctamente, continúa y ejecuta el siguiente comando para enviar tus propios cambios:</p><pre><code>git push -u origin main
</code></pre><p>Si el error persiste, obtendrás un error que dice: <code>fatal: refusing to merge unrelated histories</code>. En ese caso, utiliza la solución que se presenta en la siguiente sección.</p><h3 id="c-mo-solucionar-el-error-error-failed-to-push-some-refs-to-en-git-usando-git-pull-rebase">Cómo solucionar el error <code>error: failed to push some refs to</code> en Git usando <code>git pull --rebase</code></h3><p>El comando <code>git pull --rebase</code> es útil cuando tu rama local está un commit por detrás de la rama remota.</p><p>Para solucionar el error, ejecuta los siguientes comandos:</p><pre><code>git pull --rebase origin main
git push -u origin main
</code></pre><p>Si el primer comando se ejecuta correctamente, deberías obtener una respuesta que diga: <code>Successfully rebased and updated refs/heads/main</code>.</p><p>El segundo comando envía el estado actual de tu repositorio local a la rama remota.</p><h2 id="resumen">Resumen</h2><p>En este artículo, abordamos el error <code>error: failed to push some refs to [repositorio remoto]</code>.</p><p>Este error se produce cuando intentas enviar tus cambios locales al repositorio remoto sin actualizar tu repositorio local con los cambios más recientes del repositorio remoto.</p><p>Discutimos dos comandos que puedes usar para solucionar este error: <code>git pull origin [rama]</code> y <code>git pull --rebase origin [rama]</code>.</p><p>Espero que esto te ayude a resolver el error.</p><p>¡Feliz codificación!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo funciona internamente la autenticación SSH con GitHub ]]>
                </title>
                <description>
                    <![CDATA[ El SSH (Shell Seguro) es un protocolo cliente-servidor para conectarse y autenticarse a un servidor remoto. La autenticación significa que el servidor remoto puede verificar que eres tú mismo no alguien más hablando por ti. Podrías ya estar usando la autenticación SSH de Github, ¿pero sabes cómo funciona en sí? ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-funciona-internamente-la-autenticacion-ssh-con-github/</link>
                <guid isPermaLink="false">67c4da6aa3e1830472d3e6b6</guid>
                
                    <category>
                        <![CDATA[ SSH ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Git ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Tue, 15 Apr 2025 14:30:46 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2025/03/aba38efa-117c-4ef7-a844-91599c0a4d62.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/ssh-authentication-with-github-under-the-hood/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How SSH Authentication with GitHub Works Under the Hood</a>
      </p><p>El SSH (Shell Seguro) es un protocolo cliente-servidor para conectarse y autenticarse a un servidor remoto.</p><p>La autenticación significa que el servidor remoto puede verificar que eres tú mismo no alguien más hablando por ti.</p><p>Podrías ya estar usando la autenticación SSH de Github, ¿pero sabes cómo funciona en sí? En este artículo, aprenderá qué sucede de forma interna y cómo funciona en realidad la autenticación de SSH.</p><p>A lo largo, entenderás los conceptos fundamentales de criptografía que cada desarrollador debería saber: encriptación de clave simétrica, encriptación de clave asimétrica, funciones hash criptográficas, y firmas digitales.</p><p>Algunos desarrolladores usualmente no tienen la chance de aprender y entender estos fundamentos de criptografía, pero estos conceptos te ayudarán a la larga. También, te ayudarán en estar en una mejor posición para tomar decisiones de seguridad informadas para tus aplicaciones de web en producción.</p><p>Así que vamos, abróchate los cinturones de seguridad, ¡y comencemos!</p><h3 id="esto-es-lo-que-cubriremos-"><strong>Esto es lo que cubriremos:</strong></h3><ol><li><a href="#primero-por-que-autenticacion-importante">Primero, ¿por qué la Autenticación es tan importante?</a></li><li><a href="#encriptacion-clave-simetrica">Encriptación de Clave Simétrica</a> </li><li><a href="#encriptacion-llave-asimetrica">Encriptación de Clave Asimétrica</a></li><li><a href="#funciones-hash-criptograficas">Funciones de Hash Criptográficas</a> </li><li><a href="#firmas-digitales">Firmas Digitales</a></li><li><a href="#como-funciona-la-autenticacion-ssh">Cómo Funciona la Autenticación SSH</a> </li><li><a href="#resumiendo-todo">Resumiendo Todo</a></li></ol><!--kg-card-begin: html--><h2 id="primero-por-que-autenticacion-importante">Primero, ¿por qué la Autenticación es tan importante?</h2><!--kg-card-end: html--><p>Cuando ejecutamos <code>git push</code>, Github necesita verificar que la persona correcta está interactuando con Github. Imagina si un atacante podría llegar a hacer <code>git push</code> por ti.</p><p>Entonces todos tus repositorios estarían bajo el control del atacante. Podrían eliminar todo tu código juntamente con todo el historial de las confirmaciones.</p><p>Esto suena bastante peligroso, ¿no? Así que para verificar que eres tú el que está hablando con Github, y no un atacante, Github tiene varias formas de autenticarte.</p><p>El método más usado para autenticar con Github es la autenticación SSH.</p><p>Antes que entendamos cómo funciona la autenticación SSH internamente, necesitaremos entender los conceptos fundamentales de criptografía, a saber – encriptación de clave simétrica, encriptación de clave asimétrica, funciones de hash criptográficas, y firmas digitales.</p><p>¡Comencemos!</p><!--kg-card-begin: html--><h2 id="encriptacion-clave-simetrica">Encriptación de Clave Simétrica</h2><!--kg-card-end: html--><p>En los tiempos antiguos, los gobernantes idearon varios métodos de comunicar mensajes militares secretos a sus comandantes del ejército.</p><p>En los métodos más primitivos, mayormente usados por los gobernantes Griegos antiguos y posiblemente luego los Romanos, involucrados usando una vara de madera cilíndrica llamado un <strong><a href="https://es.wikipedia.org/wiki/Esc%C3%ADtala">Escítala</a></strong>.</p><p>Antes de una invasión militar, el gobernante tendría las dos mismas varas de madera cilíndricos exactas. Entonces le daría una escítala al comandante del ejército y mantener uno para sí mismo.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734514827027/b4945c3a-64d4-458b-a410-f23b1a08d9ef.png" class="kg-image" alt="A scytale with leather strip wounded and a message written on it." width="2012" height="1176" loading="lazy"></figure><p>El dispositivo funcionaba al enrollar una tira de cuero alrededor de escítala. Después de hacer esto, el gobernante escribiría el mensaje por encima de la tira de cuero enrollada de forma que solo pudiera ser leído cuando estuviere enrollada de forma apropiada nuevamente. </p><p>Supón que la escítala le permitiera escribir tres letras alrededor en un circulo y cinco letras de forma recta a lo largo de su longitud. La tira de cuero enrollada con el mensaje <code>attackfromright</code> escrito luciría así:</p><pre><code class="language-plaintext">       |   |   |   |   |   |
       | a | t | t | a | c |  |
     __| k | f | r | o | m |__|
    |  | r | i | g | h | t |
    |  |   |   |   |   |   |
</code></pre><p>Después de escribir el mensaje en la escítala, el gobernante desenrollaría la tira de cuero y lo enviaría al comandante del ejército. Cuando estaba desenrollada, la tira de cuero tendría el siguiente mensaje desordenado:</p><pre><code class="language-plaintext">----------------
akrtfitrgaohcmt
----------------
</code></pre><p>Así que ahora lo ves, incluso si la tira de cuero fuera interceptada por un espía enemigo, el mensaje no tendría sentido. ¿No es fascinante? El uso inteligente de una vara de madera y una tira de cuero, ¡podría haber ayudado a algunos gobernantes antiguos ganar batallas!</p><p>Cuando la tira de cuero alcanzaba al comandante del ejército, lo envolvería con su propia escítala (que sería exactamente el mismo que el del gobernante), y luego el comandante sería capaz de entender el mensaje apropiadamente.</p><p>Esta técnica de la escítala en realidad es un ejemplo de la encriptación de clave simétrica en la práctica.</p><p>La encriptación es un proceso en el que el mensaje origina es modificado (o codificado) de tal forma que solamente el recipiente indicado puede decodificar y ver el mensaje actual.</p><p>El mensaje original se llama texto plano, mientras que el mensaje codificado se llama texto cifrado. La encriptación convierte el <code>texto plano a texto cifrado</code> con la ayuda de una clave.</p><p>Para descifrar el mensaje, que sería convertir el <code>texto cifrado a texto plano</code>, una persona debe tener acceso a la misma llave.</p><p>Si lo comparamos a la técnica de la escítala, la escítala es la llave. El gobernantes solamente comparte la llave (escítala) con el comandante del ejército que necesita saber lo que dice el mensaje.</p><p>Así es como luce el proceso de encriptación:</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734519516607/75c926a3-faec-402a-8bcd-122039f47a01.png" class="kg-image" alt="Encryption with scytale as key." width="2023" height="526" loading="lazy"></figure><p>El proceso de descifrado lucirá así:</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734519525487/de096889-332c-4482-b2df-b28ce609a8a6.png" class="kg-image" alt="Decryption with scytale as key." width="1979" height="526" loading="lazy"></figure><p>Llamamos a esto encriptación de llave simétrica porque la misma llave se usa tanto para encriptar y descifrar el mensaje.</p><p>Esta llave (la escítala) se debe mantener resguardada del acceso del enemigo. Si el enemigo accede a esta llave, entonces será capaz de descifrar los mensajes.</p><p>Pero hay otro tipo de encriptación llamada encriptación de llave asimétrica. Ahora que entiendes la encriptación de llave simétrica, sigamos con la encriptación de llave asimétrica.</p><!--kg-card-begin: html--><h2 id="encriptacion-llave-asimetrica">Encriptación de Llave Asimétrica</h2><!--kg-card-end: html--><p>En la encriptación de llave simétrica, como vimos arriba, la misma llave era usada por el gobernante y el comandante del ejército para encriptar y descifrar el mensaje.</p><p>Pero en una encriptación de llave asimétrica, hay dos llaves (llamado un par de llaves). De las dos llaves, una es una llave privada y la otra es una llave pública.</p><p>La llave pública puede ser compartido con todos (el cual por eso se llama pública). Pero la llave privada tiene que mantenerse, ¡en secreto! Nunca debe ser revelado a nadie.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735200860039/7aca8ffa-c33a-44e5-ab1a-181492ebefd8.png" class="kg-image" alt="Public key can be shared with everyone. But the private key must be kept secret." width="2714" height="1428" loading="lazy"></figure><p>Lo interesante sobre la encriptación de llave asimétrica es eso, si un mensaje es encriptado con la llave pública, entonces puede ser descifrado solamente con la llave privada correspondiente. Ninguna otra llave puede descifrarlo.</p><p>Y funciona de la otra forma también. Si un mensaje es encriptado con la llave privada entonces puede ser descifrado solamente usando la llave pública correspondiente.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735120077350/b90901c8-b55c-428a-8eb4-1b8ffa65fa06.png" class="kg-image" alt="Illustration of public and private key mathematically linked with each other." width="948" height="835" loading="lazy"></figure><p>Las dos llaves – pública y privada – están enlazados matemáticamente uno con el otro. Mientras uno encripta, el otro descifra.</p><p>Sólo una pequeña nota, la encriptación de llave asimétrica también se le llama encriptación de llave pública. Estos dos términos son usados de forma intercambiable pero significa lo mismo.</p><!--kg-card-begin: html--><h2 id="funciones-hash-criptograficas">Funciones Hash Criptográficas</h2><!--kg-card-end: html--><p>Una función hash criptográfica está diseñada para tomar una entrada de cualquier longitud y producir una salida de longitud fijada. La salida de longitud fijada se le denomina valor hash.</p><p>Un ejemplo popular de una función hash criptográfica es SHA-256.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735030835833/201640c6-13b4-4b2b-9be3-88e245269bd1.png" class="kg-image" alt="SHA-256 calculation of &quot;freeCodeCamp.org&quot;" width="2283" height="285" loading="lazy"></figure><p>La imagen de arriba muestra el valor hash SHA-256 de la entrada "freeCodeCamp.org". Las funciones hash criptográficas tienen tres propiedades que lo hacen muy útiles (lo veremos en las próximas secciones).</p><p>Primero, es prácticamente imposible para tomar el valor hash y averiguar la entrada del valor hash.</p><p>Por ejemplo, si nos dan el valor hash <code>c9c31315ef2257e4b7698</code>, no hay forma de que averigüemos que la entrada a la función hash fuere "freeCodeCamp.org".</p><p>Segundo, si pasamos la misma entrada a la función hash, obtenemos el mismo valor hash como la salida.</p><p>Si pasamos "freeCodeCamp.org" nuevamente a la función hash SHA-256, obtendremos la misma salida hash como nuestra llamada previa.</p><p>Tercero, dos entradas distintas nunca comparten el mismo valor hash. Inclusive el cambio más ligero en la entrada produce una salida totalmente distinta.</p><p>Supón que si proveemos "freeCodeCamp" como entrada en vez de "freeCodeCamp.org" – obtenemos una salida totalmente distinta.</p><!--kg-card-begin: html--><h2 id="firmas-digitales">Firmas Digitales</h2><!--kg-card-end: html--><p>En tus vidas diarias, podrías tener que firma varios documentos. Podrían ser documentos legales, o el boletín de calificaciones de tus hijos, o tal vez algo más.</p><p>Cuando tu firma esté presente en el documento, le transmite a la otra parte que eres tú el que está de acuerdo cualquier cosa que esté escrito en ese documento.</p><p>Luego, no puedes dar marcha atrás de lo que está escrito en el documento. ¿Correcto?</p><p>De forma similar, en el mundo digital, tenemos las firmas digitales – o podemos simplemente llamarlos firmas.</p><p>Entendamos cómo funcionan las firmas usando un ejemplo. Tenemos dos usuarios llamados "Alice" y "Bob".</p><p>Bob quiere transferir algo de dinero a la cuenta bancaria de Alice. Así que Bob le pide a Alice su información de cuenta bancaria.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735042150046/034d26c5-b33d-4b82-aeb8-173e47cd8e8e.png" class="kg-image" alt="An illustration showing alice and bob's computers far away from each other and alice's bank account number." width="1972" height="1410" loading="lazy"></figure><p>Alice sabe sobre las firmas digitales y decidió usar una. Al final, entenderás por qué Alice optó por una firma digital.</p><p>Antes de que Alice pueda crear una firma digital. Alice provee a Bib su clave pública (y guarda la clave privada para sí misma).</p><p>Luego Alice crea una firma digital y lo color al final del documento.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735041977880/35313148-8820-42d7-b122-3ddf0cbaa723.png" class="kg-image" alt="Process of digital signature generation." width="1065" height="471" loading="lazy"></figure><p>Una firma digital se crea primero al pasar el contenido del documento a una función hash criptográfica como SHA-256. En el caso de Alice, el contenido del documento es su número de cuenta bancaria.</p><p>Una vez que obtenemos el valor hash, se encripta con la <strong>clave privada</strong> de Alice. La salida de esta encriptación es la firma el cual se coloca al final del documento.</p><p>Luego esto se lo envía a Bob por Internet.</p><p>Cuando Bob recibe este documento, verifica si la <strong>firma es válida o no</strong>.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735043216695/256f7707-3f40-433f-9b00-c11b27ef01e8.png" class="kg-image" alt="Process of signature verification." width="2905" height="1636" loading="lazy"></figure><p>Para verificar la firma, Bob primero descifra la firma con la clave pública de Alice. Si lo recuerdas, Alice generó la firma al encriptar el valor hash.</p><pre><code class="language-plaintext"> plaintext                         ciphertext  
     |                                 |
     |                                 |
     |                                 |
hash value --------encrypt--------&gt; signature
</code></pre><p>Así que, cuando Bob descifra la firma, obtendrá el valor hash que Alice calculó. Llamemos a este valor hash de Alice.</p><pre><code class="language-plaintext"> ciphertext                         plaintext  
     |                                 |
     |                                 |
     |                                 |
signature --------decrypt--------&gt; hash value
</code></pre><p>Luego Bob toma el número de la cuenta bancaria que esta presente en el documento y lo pasa a la función hash.</p><p>Finalmente, Bob coincide el valor hash de Alice (la firma descifrada) y el valor hash que calculó. Si ambos valores hash coinciden entonces significa que la firma es válida.</p><p>Muy bien – ¿pero por qué necesitábamos hacer todo esto? ¿Qué significa si la firma es válida?</p><p>Cuando la verificación de la firma es exitosa, prueba dos cosas.</p><p>Primero, prueba que el documento ha sido enviado por Alice solamente. Nadie más podría haber enviado este documento.</p><p>La garantía de que solamente Alice ha enviado este documento viene del hecho de que fuimos capaces de descifrar la firma usando la clave pública de Alice.</p><p>Hemos aprendido de que si algo es encriptado usando una clave privada entonces puede ser descifrado solamente usando su clave pública vinculada.</p><p>Asi que, si Bob fue capaz de descifrar la firma usando la clave pública de Alice, significa que fue descifrado utilizando la clave privada de Alice, ¿correcto?</p><p>Y solamente Alice tiene acceso a su clave privada. Esto significa que, ¡Alice es la única persona quien podría haber enviado este documento!</p><p>Segundo, prueba que el contenido del mensaje no ha sido modificado por un atacante durante la transmisión en red.</p><p>Hicimos dos cosas para verificar la firma. Desciframos la firma, y nos dio el valor hash que Alice calculó. Y también hasheamos el número de cuenta bancaria recibido.</p><p>Si el valor hash que Alice caluló y el valor hash que Bob calculó son los mismos, significa que Alice y Bob dieron exactamente la misma entrada a la función hash.</p><p>Y esto significa que el número de cuenta bancaria que Alice envió y que Bob recibió son exactamente los mismos.</p><p>Si un atacante habría cambiado el número de cuenta bancaria antes de que el documento le llegara a Bob, entonces Bob hubiera recibido un número de cuenta bancaria modificado.</p><p>Cuando Bob fuera a calcular el valor hash de este número de cuenta bancaria modificado, el valor hash hubiera sido distinto de lo que Alice había calculado.</p><p>Así que mientras coincida el valor hash de Alice (firma descifrada) y el valor hash que Bob calculó, la coincidencia fallaría. Y evitaría que Bob transfiera dinero al número de cuenta bancaria errónea.</p><p>Para concluir, cuando la firma es verificada con éxito, significa que:</p><ol><li>El documento es solamente de Alice.</li><li>El contenido del documento no fue modificado por algún tercero.</li></ol><p>Ahora has aprendido sobre encriptación de clave simétrica, encriptación de clave asimétrica, funciones de hash criptográficas, y firmas digitales. ¡Es genial!</p><p>Hemos construido una fundación realmente sólido. Ahora entender la autenticación SSH va a ser mucho más fácil para ti.</p><!--kg-card-begin: html--><h2 id="como-funciona-la-autenticacion-ssh">Cómo Funciona la Autenticación SSH</h2><!--kg-card-end: html--><p>Si no has configurado la autenticación SSH con Github, entonces después de terminar este artículo puedes seguir la <a href="https://docs.github.com/es/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account">documentación detallada de Github sobre cómo hacerlo</a>. Por ahora, por favor quédate hasta el final.</p><p>La cruz del proceso de configuración es que crees un par de claves pública y privada en tu computadora local. Luego subes tu clave pública a tu perfil de Github – ¡y eso es todo!</p><p>Después que hemos creado nuestro par de claves pública y privada, en Ubuntu, el par de claves se almacenan dentro de la carpeta <code>~/.ssh</code>.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735035539565/1f837d9b-9717-44fa-a5e0-5801276113df.png" class="kg-image" alt="Showing my public key from my terminal." width="4380" height="243" loading="lazy"></figure><p>La imagen de arriba muestra mi clave pública. Tengo esta clave pública cargada en mi perfil de Github:</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735035898284/1ef9133a-895b-4847-a7ac-6157fdcc3143.png" class="kg-image" alt="Showing my GitHub profile settings where my public key is uploaded for SSH authentication with GitHub." width="4287" height="1398" loading="lazy"></figure><p>Ahora, cuando ejecuto <code>git push</code> o cualquier otro comando que se quiera comunicar con Github, estaré autenticado usando la autenticación SSH.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735053545173/6fb293f1-f90a-4b64-b026-082d8676afae.png" class="kg-image" alt="The illustration of SSH authentication process between client and GitHub server." width="4082" height="2574" loading="lazy"></figure><p>SSH es un protocolo cliente-servidor. Nuestra computadora que ejecuta <code>git push</code> es el cliente SSH. Github es el servidor SSH.</p><p>El cliente comienza con el proceso de autenticación al solicitar nuestra clave pública que tenemos dentro de <code>~/.ssh</code>.</p><p>Luego el cliente prepara un mensaje el cual tiene nuestra clave pública. Y luego el cliente genera la firma usando la clave privada correspondiente.</p><p>La clave pública y la firma se envían a Github. Al recibir este mensaje, Github hace dos cosas:</p><p>Primero, verifica si la clave pública mencionada en el mensaje se conecta a un perfil de Gihtub o no. Ya que subimos nuestra clave pública a Github, este paso se realizó con éxito.</p><p>Segundo, Github verifica la firma usando la clave pública que hemos subido.</p><p>Hemos aprendido que si la verificación de la firma termina siendo exitoso significa que solamente la persona quién está en posesión de la clave privada correspondiente podría haber enviado el mensaje.</p><p>Ya que solamente tenemos la clave privada vinculada a la clave pública cargada, esto prueba a Github que en realidad somos nosotros intentando comunicarse con Github y no un atacante.</p><p>Ahora, Github es 100% seguro que somos la persona correcta, estamos autenticados exitosamente, y a nuestro <code>git push</code> se le permite proceder.</p><p>Ves, se volvió fácil de entender la autenticación SSH ya que aprendiste los fundamentos.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735120630613/e9a8bbba-3cc4-43e7-8369-865ab377fb87.png" class="kg-image" alt="A xkcd comic depicting Cueball thinking to share his private key. A dangerous move!" width="733" height="282" loading="lazy"></figure><p>La imagen de arriba es del cómic popular xkcd. El personaje allí (llamado Cueball) está pensando en revelar su clave privada. Espero que ahora sepas por qué está mal revelar tu clave privada.</p><p>Si revelas tu clave privada entonces alguien más puede autenticarse a Github en tu nombre. No quieres que eso suceda, ¿verdad? ;)</p><p>Así que, siempre asegúrate de mantener tu clave privada para ti mismo nada más.</p><!--kg-card-begin: html--><h2 id="resumiendo-todo">Resumiendo Todo</h2><!--kg-card-end: html--><p>Si has llegado hasta acá, entonces felicitaciones 🥳.</p><p>Has aprendido como funciona en realidad la autenticación SSH – cuando la firma fue verificado exitosamente por Github, le confirma a Github que somos nosotros quienes le estamos hablando, no un atacante.</p><p>A lo largo adquiriste una comprensión fundamental de la encriptación de clave simétrica, encriptación de clave asimétrica, funciones hash criptográficas y firmas digitales.</p><p>Gracias por estar conmigo en esta, espero que te vayas con algunos nuevos y valiosos aprendizajes.</p><p>Pongo ideas y recursos útiles en mi Twitter. <a href="https://twitter.com/vkwebdev"><strong>Deberías seguirme allí</strong></a>. Respetaré tu tiempo.</p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
