<?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[ next.js - freeCodeCamp.org ]]>
        </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[ next.js - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/espanol/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 14 May 2026 19:58:39 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/espanol/news/tag/next-js/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Cómo crear y enviar Plantillas de Email usando React Email y Resend en Next.js ]]>
                </title>
                <description>
                    <![CDATA[ Las aplicaciones modernas de Software frecuentemente se basan en comunicación por email para interactuar con los usuarios. Podrían enviar códigos de autenticación durante intentos de inicio de sesión, emails de marketing, o boletines informativos, por ejemplo. Esto significa que las notificaciones de email son típicamente el medio más común de ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-crear-y-enviar-plantillas-de-email-usando-react-email-y-resend-en-next-js/</link>
                <guid isPermaLink="false">67cf85b759617604dd901b87</guid>
                
                    <category>
                        <![CDATA[ next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ NextJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ resend ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Tue, 15 Apr 2025 14:26:02 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2025/03/3450a9b3-e740-4362-b0ab-0269646e725c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Las aplicaciones modernas de Software frecuentemente se basan en comunicación por email para interactuar con los usuarios. Podrían enviar códigos de autenticación durante intentos de inicio de sesión, emails de marketing, o boletines informativos, por ejemplo. Esto significa que las notificaciones de email son típicamente el medio más común de comunicación con los usuarios.</p><p>En este tutorial, aprenderás cómo diseñar plantillas asombrosas de email con <a href="https://react.email/docs/introduction">React Email</a> y enviarlos usando <a href="https://resend.com/docs/send-with-nextjs">Resend</a> – una potente plataforma API de correo electrónico. </p><h2 id="pre-requisitos"><strong>Pre-requisitos</strong></h2><p>Para aprovechar lo máximo de este tutorial, deberías tener un entendimiento básico de React o Next.js.</p><p>También haremos uso de las siguientes herramientas:</p><ul><li>React Email: Una librería que te permite crear plantillas de email diseñadas hermosamente usando componentes de React.</li><li>Resend: Una poderosa y sencilla plataforma API para enviar emails desde tus aplicaciones.</li></ul><h2 id="c-mo-construir-la-aplicaci-n-con-next-js"><strong>Cómo construir la Aplicación con Next.js</strong></h2><p>En esta sección, crearás una aplicación sencilla de soporte de cliente. La aplicación incluirá un formulario para usuarios para enviar sus solicitudes, el cual dispara una notificación de email confirmando que un ticket de soporte ha sido creado.</p><p>Para comenzar, primero configuraremos la interfaz de usuario y un endpoint de la API.</p><p>Ejecuta el siguiente comando para crear un nuevo proyecto de Next.js con TypeScript:</p><pre><code class="language-bash">npx create-next-app react-email-resend
</code></pre><p>Actualiza el archivo <code>app/page.tsx</code> para renderizar un formulario que recoge los detalles del cliente, incluyendo su nombre completo, la dirección de email, el asunto del ticket, y un mensaje con detalles describiendo el problema. Cuando el formulario se envía, los datos son registrados a la consola usando la función <code>handleSubmit</code>.</p><pre><code class="language-typescript">"use client";
import support from "@/app/images/support.jpg";
import { useState } from "react";
import Image from "next/image";

export default function Page() {
    //👇🏻 estados de las entradas
    const [name, setName] = useState&lt;string&gt;("");
    const [email, setEmail] = useState&lt;string&gt;("");
    const [subject, setSubject] = useState&lt;string&gt;("");
    const [content, setContent] = useState&lt;string&gt;("");

    const handleSubmit = async (e: React.FormEvent) =&gt; {
        e.preventDefault();
        //👇🏻 registra las entradas del usuario
        console.log({ name, email, subject, content });
    };
return ({/** -- elementos de UI -- */})
}
</code></pre><p>Regresa los elementos UI del formulario que aceptan el nombre completo del usuario, la dirección de email, asunto del ticket, y un mensaje con los detalles describiendo el problema.</p><pre><code class="language-typescript">    return (
        &lt;main className='w-full min-h-screen flex items-center justify-between'&gt;
                &lt;form className='w-full' onSubmit={handleSubmit}&gt;
                    &lt;label htmlFor='name' className='opacity-60'&gt;
                        Full Name
                    &lt;/label&gt;
                    &lt;input
                        type='text'
                        className='w-full px-4 py-3 border-[1px] mb-3 border-gray-300 rounded-sm'
                        id='name'
                        required
                        value={name}
                        onChange={(e) =&gt; setName(e.target.value)}
                    /&gt;

                    &lt;label htmlFor='email' className='opacity-60'&gt;
                        Email Address
                    &lt;/label&gt;
                    &lt;input
                        type='email'
                        className='w-full px-4 py-3 border-[1px] mb-3 border-gray-300 rounded-sm'
                        id='email'
                        value={email}
                        onChange={(e) =&gt; setEmail(e.target.value)}
                        required
                    /&gt;

                    &lt;label htmlFor='subject' className='opacity-60'&gt;
                        Subject
                    &lt;/label&gt;
                    &lt;input
                        type='text'
                        className='w-full px-4 py-3 border-[1px] mb-3 border-gray-300 rounded-sm'
                        id='subject'
                        value={subject}
                        onChange={(e) =&gt; setSubject(e.target.value)}
                        required
                    /&gt;

                    &lt;label htmlFor='message' className='opacity-60'&gt;
                        Message
                    &lt;/label&gt;
                    &lt;textarea
                        rows={7}
                        className='w-full px-4 py-3 border-[1px] mb-3 border-gray-300 rounded-sm'
                        id='message'
                        required
                        value={content}
                        onChange={(e) =&gt; setContent(e.target.value)}
                    /&gt;

                    &lt;button className='w-full bg-blue-500 py-4 px-3 rounded-md font-bold text-blue-50'&gt;
                        SEND MESSAGE
                    &lt;/button&gt;
                &lt;/form&gt;
            &lt;/div&gt;
        &lt;/main&gt;
    );
</code></pre><p>Aquí está la página resultante del componente:</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733748196715/703e7e5b-f868-45e6-b62f-64a2d6dd279e.png" class="kg-image" alt="The Page component renders a form that accepts the user's input" width="2560" height="1420" loading="lazy"></figure><p>Luego, crea un endpoint de la API (<code>/api/route.ts</code>) que acepta las entradas del cliente.</p><pre><code class="language-bash">cd app
mkdir api &amp;&amp; cd api
touch route.ts
</code></pre><p>Copia el siguiente código en el archivo <code>api/route.ts</code>. El endpoint de la API registra la entrada del cliente a la consola después de recibirla.</p><pre><code class="language-typescript">import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
    const { name, email, subject, content } = await req.json();
    //👇🏻 registra los contenidos
    console.log({ name, email, subject, content });
    return NextResponse.json({
        message: "Email enviado con éxito",
        data,
 });
}
</code></pre><p>Actualiza la función <code>handleSubmit</code> para enviar los datos del cliente al endpoint de la API y regresa la respuesta JSON:</p><pre><code class="language-typescript">const handleSubmit = async (e: React.FormEvent) =&gt; {
    e.preventDefault();

    try {
        const response = await fetch("/api", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({ name, email, subject, content }),
        });
        const data = await response.json();
        alert(data.message);
    } catch (error) {
        console.error(error);
        alert("Un error ocurrió, por favor intenta nuevamente más tarde");
    }
    setName("");
    setEmail("");
    setSubject("");
    setContent("");
};
</code></pre><p>¡Felicidades! Has configurado la colección de datos y el envío. En las próximas secciones, te mostraré cómo crear y enviar plantillas de email con React Email y Resend.</p><h2 id="c-mo-crear-plantillas-de-email-usando-react-email"><strong>Cómo crear Plantillas de Email usando React Email</strong></h2><p>React Email te permite construir y enviar componentes de email usando React y TypeScript. Soporta múltiples clientes de email, incluyendo Gmail, Mail de Yahoo, Outlook, y Mail de Apple.</p><p>React Email también provee múltiples <a href="https://react.email/components">componentes UI</a> que te permiten personalizar las plantillas de email según tu diseño preferido usando componentes de React JSX/TSX.</p><p>Instala el paquete React Email y sus componentes al ejecutar el fragmento de código de abajo:</p><pre><code class="language-bash">npm install react-email -D -E
npm install @react-email/components -E
</code></pre><p>Incluye este escript en tu archivo <code>package.json</code>. Dirige a React Email a dónde están localizados las plantillas de email en tu proyecto.</p><pre><code class="language-json">  "scripts": {
    "email": "email dev --dir src/emails"
  },
</code></pre><p>Una de las características de React Email es la habilidad de pre-visualizar tu plantilla de email en tu navegador durante el desarrollo, permitiéndote ver cómo aparecerá en el email del receptor.</p><p>Así que luego, crea una carpeta <code>emails</code> que contenga un archivo <code>TicketCreated.tsx</code> dentro de la carpeta <code>src</code> de Next.js y copia el siguiente fragmento de código en el archivo:</p><pre><code class="language-typescript">import * as React from "react";
import {
    Body,
    Container,
    Head,
    Heading,
    Hr,
    Html,
    Link,
    Preview,
    Text,
    Tailwind,
} from "@react-email/components";

interface TicketCreatedProps {
    username: string;
    ticketID: string;
}

const baseUrl = process.env.VERCEL_URL || "http://localhost:3000";
</code></pre><p>En el fragmento de código de arriba, importamos los componentes necesarios para construir la plantilla de email.</p><p>Luego, agrega el componente <code>TicketCreated</code> al archivo para renderizar la plantilla de email usando los <a href="https://react.email/components">componentes de React Email</a>.</p><pre><code class="language-typescript">export const TicketCreated = ({ username, ticketID }: TicketCreatedProps) =&gt; {
    return (
        &lt;Html&gt;
            &lt;Head /&gt;
            &lt;Preview&gt;Email de Confirmación del Ticket de Soporte 🎉&lt;/Preview&gt;
            &lt;Tailwind&gt;
                &lt;Body className='bg-white my-auto mx-auto font-sans px-2'&gt;
                    &lt;Container className='border border-solid border-[#eaeaea] rounded my-[40px] mx-auto p-[20px] max-w-[465px]'&gt;
                        &lt;Heading className='text-black text-[24px] font-normal text-center p-0 my-[30px] mx-0'&gt;
                            Tu Ticket ha sido creado
                        &lt;/Heading&gt;
                        &lt;Text className='text-black text-[14px] leading-[24px]'&gt;
                        Hola {username},
                        &lt;/Text&gt;
                        &lt;Text className='text-black text-[14px] leading-[24px]'&gt;
                            &lt;strong&gt;El Ticket de Soporte&lt;/strong&gt; (&lt;Link href={`${baseUrl}/ticket/${ticketID}`} className='text-blue-600 no-underline'&gt;{`#${ticketID}`}&lt;/Link&gt;) ha sido creado con éxito.
                        &lt;/Text&gt;

                        &lt;Text className='text-black text-[14px] leading-[24px]'&gt;El Equipo de Soporte revisará tu ticket y volverán contigo en breve.
                        &lt;/Text&gt;

                        &lt;Hr className='border border-solid border-[#eaeaea] my-[26px] mx-0 w-full' /&gt;
                        &lt;Text className='text-[#666666] text-[12px] leading-[24px]'&gt;Este mensaje estaba dirigido a {" "} &lt;span className='text-black'&gt;{username}&lt;/span&gt;. Si no creaste este ticket, por favor ignora este email.
                        &lt;/Text&gt;
                    &lt;/Container&gt;
                &lt;/Body&gt;
            &lt;/Tailwind&gt;
        &lt;/Html&gt;
    );
};
</code></pre><p>Finalmente, expórtalo y agrega un valor predeterminado para las props:</p><pre><code class="language-typescript">TicketCreated.PreviewProps = {
    username: "alanturing",
    ticketID: "9083475",
} as TicketCreatedProps;

export default TicketCreated;
</code></pre><p>Ejecuta <code>npm run email</code> en tu terminal para pre-visualizar la plantilla de email.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733752824765/6c5e1518-fc85-4d79-bd05-f4a2c2381976.png" class="kg-image" alt="6c5e1518-fc85-4d79-bd05-f4a2c2381976" width="2553" height="1276" loading="lazy"></figure><p>Esta plantilla de email notifica a los clientes que su ticket de soporte ha sido creado y que alguien del equipo de soporte se comunicarán con ellos.</p><p>React Email ofrece una variedad de plantillas de email pre-diseñada, facilitando el crear email con estilos hermosos para distintos propósitos. Puedes revisar el <a href="https://demo.react.email/preview/notifications/vercel-invite-user">demo disponible para ver ejemplos</a> de lo que es posible.</p><h2 id="c-mo-enviar-emails-con-resend"><strong>Cómo enviar Emails con Resend</strong></h2><p>Resend es una API de email sencilla que te permite enviar emails dentro de tu aplicación de software. Soporta un montón de lenguajes de programación, incluyendo JavaScript (Next.js, Express, Node.js), Python, PHP, Go, and Rust, entre otros.</p><p>Resend y React Email pueden ser fácilmente integrados juntamente ya que el co-fundador de Resend, <a href="https://github.com/bukinoshita">Bu Kinoshita</a>, es también el creador de React Email.</p><p>Crea una cuenta en <a href="https://resend.com/docs/send-with-nextjs">Resend</a>. Una vez que iniciaste sesión, navega hacia la sección de claves de API en tu panel y copia tu clave de API en un archivo <code>.env.local</code>.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733753483127/4a289abc-b7e5-4c81-b7ee-e8f084354fae.png" class="kg-image" alt="4a289abc-b7e5-4c81-b7ee-e8f084354fae" width="2560" height="1384" loading="lazy"></figure><pre><code class="language-bash">//👇🏻 archivo .env.local 
RESEND_API_KEY=&lt;RESEND_API_KEY&gt;
</code></pre><p>Actualiza el endpoint de la API para enviar un email usando la plantilla de React Email, como se muestra abajo:</p><pre><code class="language-typescript">import { NextRequest, NextResponse } from "next/server";
//👇🏻 función generado del ID del boleto
import { v4 as generateID } from "uuid";
//👇🏻 importa la plantilla de email
import TicketCreated from "@/emails/TicketCreated";
//👇🏻 imports Resend
import { Resend } from "resend";
const resend = new Resend(process.env.RESEND_API_KEY);

export async function POST(req: NextRequest) {
    //👇🏻 acepta la entrada del cliente desde el frontend
    const { name, email, subject, content } = await req.json();
    //👇🏻 los registra
    console.log({ name, email, subject, content });
    //👇🏻 envia una email usando la plantilla de email
    const { data, error } = await resend.emails.send({
        from: "Acme &lt;onboarding@resend.dev&gt;",
        to: [email],
        subject: "Email de Confirmación del Ticket 🎉",
        react: TicketCreated({ username: name, ticketID: generateID() }),
    });

    if (error) {
        return NextResponse.json(
            { message: "Hubo un error al enviar el email" },
            { status: 500 }
        );
    }

    return NextResponse.json({
        message: "El email se envió con éxito",
        data,
    });
}
</code></pre><p>¡Felicidades!🥳 Has completado este tutorial.</p><p>Aquí un breve demo de la aplicación:</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.25%;" class="fluid-width-video-wrapper">
            <iframe width="560" height="315" src="https://www.youtube.com/embed/RsNAtwDjAEg" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 27.5px; vertical-align: middle; aspect-ratio: 16 / 9; width: 720px; height: auto;" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><h2 id="pr-ximos-pasos"><strong>Próximos Pasos</strong></h2><p>En este tutorial, aprendiste cómo crear plantillas de email con React Email y enviarlos usando Resend. Ambos paquetes te permiten integra la comunicación de email fácilmente con tus aplicaciones.</p><p>Ya sean simples las notificaciones de email, los boletines informativos, o campañas de marketing, React Email y Resend ofrecen una solución eficiente y personalizable para satisfacer tus necesidades.</p><p>Algunos recursos útiles incluyen:</p><ul><li><a href="https://react.email/components">Componentes pre-construidas de React Email</a></li><li><a href="https://react.email/docs/introduction">Documentación de React Email</a></li><li><a href="https://resend.com/docs/send-with-nextjs">Cómo instalar Resend en aplicaciones de Next.js</a></li></ul><p>¡Gracias por leer!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo crear una aplicación Fullstack con Next.js 13 y Firebase ]]>
                </title>
                <description>
                    <![CDATA[ > Nota: Al momento de escribir este artículo (17 de Febrero de 2023), la versión 13 de Next.js fue lanzado, el 25 de Octubre de 2022 para ser exacto.  El equipo detrás de Next.js ha lanzado recientemente Next.js 13 [https://nextjs.org/blog/next-13], que tiene un montón de características nuevas como un ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-crear-una-aplicacion-full-stack-con-next-js-13-y-firebase/</link>
                <guid isPermaLink="false">66c75e5ca244b004a5d60a64</guid>
                
                    <category>
                        <![CDATA[ next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Firebase ]]>
                    </category>
                
                    <category>
                        <![CDATA[ full stack ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Constanza Areal ]]>
                </dc:creator>
                <pubDate>Thu, 19 Sep 2024 21:32:31 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/09/pexels-sevenstorm-juhaszimrus-443383--1-.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <blockquote><strong>Nota</strong>: Al momento de escribir este artículo (17 de Febrero de 2023), la versión 13 de Next.js fue lanzado, el 25 de Octubre de 2022 para ser exacto. </blockquote><p>El equipo detrás de Next.js <a href="https://nextjs.org/blog/next-13">ha lanzado recientemente Next.js 13</a>, que tiene un montón de características nuevas como un nuevo directorio <code>app</code>, componentes para el lado del servidor y del cliente, entre otros.</p><p>En este artículo, aprenderás cómo usar el nuevo Next.js 13 y la Base de Datos Firebase para crear una aplicación <em>full stack.</em></p><p>Antes de continuar, para poder seguir el artículo necesitas tener un conocimiento básico de JavaScript, React y Next.js. Si necesitas repasar alguno de esos conceptos, aquí tienes algún recursos para principiantes:</p><ul><li><a href="https://www.freecodecamp.org/news/learn-javascript-interactive-course/">Aprende JavaScript - contenidos y curso interactivo [en inglés]</a></li><li><a href="https://www.freecodecamp.org/news/learn-react-js-in-this-free-7-hour-course/">Aprende React - curso completo [en inglés]</a></li><li><a href="https://www.freecodecamp.org/news/the-next-js-handbook/">Aprende Next.js - cuadernillo completo [en inglés]</a></li></ul><p>Si estás listo, empecemos.</p><h2 id="c-mo-crear-un-nuevo-proyecto-de-next-js-13"><strong>Cómo crear un nuevo proyecto de Next.js 13</strong></h2><p>Para instalar Next.js, necesitas tener instalado Node.js y npm/yarn instalado en tu computadora. Si no los tienes, puedes bajarlos desde sus sitios oficiales: <a href="https://nodejs.org/en/">Node.js website</a> y <a href="https://www.npmjs.com/">npm website</a> (aunque npm está incluido al instalar Node).</p><ol><li>En el directorio que quieras, inicia la terminal de tu computadora y ejecuta el siguiente comando: <code>npx create-next-app@13 --experimental-app</code>.</li><li>Elige el nombre de tu proyecto y presiona enter para crearlo y esperar a que lo instale.</li><li>Se creará un nuevo directorio con el nombre de tu proyecto con todos archivos necesarios.</li><li>Ejecuta el comando cd para cambiar al directorio nuevo: <br><code>cd my-project-name</code></li><li>Y para iniciar el servidor de desarrollo<strong>, </strong>ejecuta el siguiente comando:</li></ol><pre><code class="language-bash">// si estás usando yarn
yarn run dev

// si estás usando npm
npm run dev</code></pre><p>6. Ejecutar este comando iniciará el servidor de desarrollo para que puedas ver tu aplicación en tiempo real en <a href="http://localhost:3000/">http://localhost:3000</a>.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot-2023-02-15-at-5.33.52-PM.png" class="kg-image" alt="Screenshot-2023-02-15-at-5.33.52-PM" width="600" height="400" loading="lazy"></figure><p><strong>Cómo instalar Firebase en Next.js</strong></p><p>Firebase es una plataforma Baas -<em> Backend-as-a-Service</em>, por su sigla en inglés- que ofrece servicios como autenticación, bases de datos en tiempo real, almacenamiento en la nube, análisis de datos, entre otros.</p><p>En este tutorial, usaremos Firebase como nuestra base de datos. Sigue estos pasos para crear una aplicación Firebase:</p><ol><li>En <a href="https://console.firebase.google.com/">https://console.firebase.google.com/</a>, inicia sesión con tu cuenta de Google.</li><li>Haz click en <strong>Crear proyecto</strong> y ponle un nombre. Luego, haz clic en <strong>Continuar</strong>.</li><li>En la pantalla siguiente puedes elegir si quieres habilitar métodos de análisis de datos para tu proyecto.</li><li>Haz clic en <strong>Crear proyecto.</strong></li></ol><p>Luego, necesitas crear una aplicación web. En la página de inicio, haz clic en el ícono de web para crear la aplicación:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot-2023-02-15-at-5.40.33-PM.png" class="kg-image" alt="Screenshot-2023-02-15-at-5.40.33-PM" width="600" height="400" loading="lazy"></figure><p>Ponle un nombre a tu aplicación y haz clic en <strong>Registrar app:</strong></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot-2023-02-15-at-5.40.48-PM.png" class="kg-image" alt="Screenshot-2023-02-15-at-5.40.48-PM" width="600" height="400" loading="lazy"></figure><p>Copia el archivo de configuración ya que vamos a necesitarlo luego. Haz clic en continuar hasta que termines.</p><p>Una vez que estés de vuelta en la página de inicio de tu proyecto, elige un producto que quieras agregar en tu aplicación. Para este tutorial, sólo agrega <strong>Authentication</strong> y <strong>Cloud Firestore</strong>:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot-2023-02-15-at-6.33.34-PM.png" class="kg-image" alt="Screenshot-2023-02-15-at-6.33.34-PM" width="600" height="400" loading="lazy"></figure><p>Para <strong>Authentication</strong><em>, </em>elige <strong>Método de acceso </strong>y agrega <strong>Correo electrónico/contraseña</strong>.</p><p>Después de instalar exitosamente Firebase, ya lo podemos usar como back-end en tu aplicación de Next.js 13.</p><p>Para usar Firebase con Next.js, sigue estos pasos.</p><p>Instala la última versión del SDK de Firebase en tu proyecto Next.js ejecutando el siguiente comando en tu terminal: </p><pre><code class="language-js">yarn add firebase

// o si estás usando npm
npm install firebase
</code></pre><p>Crea un archivo <code>.env</code> en el directorio raíz de tu proyecto Next.js y copia tus archivos de configuración de Firebase (los que habías copiado antes). Debería verse así:</p><pre><code class="language-js">NEXT_PUBLIC_FIREBASE_API_KEY=api-key
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=auth-domain
NEXT_PUBLIC_FIREBASE_PROJECT_ID=project-id
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=storage-bucket
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=sender-id
NEXT_PUBLIC_FIREBASE_APP_ID=app-id
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=analytic-id
</code></pre><p>Luego, para que se vea todo más prolijo, en tu directorio <strong>src</strong> crea una carpeta con el nombre <strong>firebase</strong> y crea un archivo <code>config.js</code> con el siguiente código;</p><pre><code class="language-js">// Importa las funciones que necesites de los SDKs 
import { initializeApp, getApps } from "firebase/app";

const firebaseConfig = {
    apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
    authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
    projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
    storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
    messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
    appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
    measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
};

// Inicializa Firebase
let firebase_app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApps()[0];

export default firebase_app;
</code></pre><p>Con esto ya listo, ya puedes utilizar Firebase como base de datos en tu aplicación Next.js.</p><h2 id="c-mo-instalar-authentication">Cómo instalar Authentication</h2><p>Al querer construir aplicaciones <em>fullstack</em>, lo primero en lo que pensamos es la autenticación de usuarios. Con Firebase, podemos crear una forma de registrar usuarios o de que inicien sesión muy fácilmente.</p><p>En tu directorio <strong>src &gt; firebase</strong>, crea una nueva carpeta con el nombre <strong>auth</strong>. Todo el código relacionado con la autenticación de firebase lo guardaremos aquí.</p><p>Luego, crea un archivo <code>signup.js</code> &nbsp;dentro del directorio <strong>src &gt; firebase &gt; auth </strong>con el siguiente código:</p><pre><code class="language-js">import firebase_app from "../config";
import { createUserWithEmailAndPassword, getAuth } from "firebase/auth";

const auth = getAuth(firebase_app);


export default async function signUp(email, password) {
    let result = null,
        error = null;
    try {
        result = await createUserWithEmailAndPassword(auth, email, password);
    } catch (e) {
        error = e;
    }

    return { result, error };
}

</code></pre><p>Vamos a descrifrar este código. Lo que estamos haciendo aquí es exportando la función <code>signUp()</code> que usa el método <strong>createUserWithEmailAndPassword() </strong>de Firebase para poder registrar nuevos usuarios. Luego de hacer esto, vamos a poder usar la función <code>signUp()</code> en cualquier lado de nuestra app.</p><p>En la misma carpeta, agreguemos nuestra función <code>signIn()</code>. Para hacer esto, crea un archivo <code>signin.js</code> con el siguiente código:</p><pre><code class="language-js">import firebase_app from "../config";
import { signInWithEmailAndPassword, getAuth } from "firebase/auth";

const auth = getAuth(firebase_app);

export default async function signIn(email, password) {
    let result = null,
        error = null;
    try {
        result = await signInWithEmailAndPassword(auth, email, password);
    } catch (e) {
        error = e;
    }

    return { result, error };
}
</code></pre><h3 id="c-mo-crear-las-p-ginas-para-el-inicio-de-sesi-n-y-el-registro-de-usuarios-en-next-js">Cómo crear las páginas para el inicio de sesión y el registro de usuarios en Next.js</h3><p>En Next.js 13 se puede crear nuevas páginas dentro del directorio <code>app</code>. Cada página es una carpeta con un archivo <code>page.js</code>. Para aprender más sobre cómo crear páginas, puedes leer la <a href="https://beta.nextjs.org/docs/routing/pages-and-layouts">documentación de Next.js al respecto [en inglés]</a>.</p><p>Para crear un página de registro de usuarios un nuevo archivo <code>page.js</code> en <strong>signup</strong>, dentro de tu directorio <strong>app</strong>. Luego, agrega el siguiente código: </p><pre><code class="language-jsx">'use client'
import React from "react";
import signUp from "@/firebase/auth/signup";
import { useRouter } from 'next/navigation'

function Page() {
    const [email, setEmail] = React.useState('')
    const [password, setPassword] = React.useState('')
    const router = useRouter()

    const handleForm = async (event) =&gt; {
        event.preventDefault()

        const { result, error } = await signUp(email, password);

        if (error) {
            return console.log(error)
        }

        // else successful
        console.log(result)
        return router.push("/admin")
    }
    return (&lt;div className="wrapper"&gt;
        &lt;div className="form-wrapper"&gt;
            &lt;h1 className="mt-60 mb-30"&gt;Sign up&lt;/h1&gt;
            &lt;form onSubmit={handleForm} className="form"&gt;
                &lt;label htmlFor="email"&gt;
                    &lt;p&gt;Email&lt;/p&gt;
                    &lt;input onChange={(e) =&gt; setEmail(e.target.value)} required type="email" name="email" id="email" placeholder="example@mail.com" /&gt;
                &lt;/label&gt;
                &lt;label htmlFor="password"&gt;
                    &lt;p&gt;Password&lt;/p&gt;
                    &lt;input onChange={(e) =&gt; setPassword(e.target.value)} required type="password" name="password" id="password" placeholder="password" /&gt;
                &lt;/label&gt;
                &lt;button type="submit"&gt;Sign up&lt;/button&gt;
            &lt;/form&gt;
        &lt;/div&gt;
    &lt;/div&gt;);
}

export default Page;`
</code></pre><p>Por defecto, cada página que se agrega al directorio <code>page.js</code> es un <a href="https://beta.nextjs.org/docs/rendering/server-and-client-components">componente del servidor [enlace a la documentación en inglés]</a> y por lo tanto no permite agregar interactividad de lado del servidor como un <code>onSubmit()</code> a elementos de formulario. Para poder agregar esta interactividad, tenemos que decirle a Next.js que queremos un componente Cliente. Para hacer esto, añadimos lo siguiente al comienzo del archivo y antes de importar cualquier otro elemento:</p><pre><code class="language-js">'use client'

// código del componente
</code></pre><p>Del mismo modo vamos a crear nuestra página de inicio de sesión. Para crear esta página, crea un archivo <strong>page.js</strong> en <strong>signin</strong>, dentro de tu directorio <strong>app</strong>, y agrega el siguiente código:</p><pre><code class="language-jsx">'use client'
import React from "react";
import signIn from "@/firebase/auth/signin";
import { useRouter } from 'next/navigation'

function Page() {
    const [email, setEmail] = React.useState('')
    const [password, setPassword] = React.useState('')
    const router = useRouter()

    const handleForm = async (event) =&gt; {
        event.preventDefault()

        const { result, error } = await signIn(email, password);

        if (error) {
            return console.log(error)
        }

        // sino exitoso
        console.log(result)
        return router.push("/admin")
    }
    return (&lt;div className="wrapper"&gt;
        &lt;div className="form-wrapper"&gt;
            &lt;h1 className="mt-60 mb-30"&gt;Sign up&lt;/h1&gt;
            &lt;form onSubmit={handleForm} className="form"&gt;
                &lt;label htmlFor="email"&gt;
                    &lt;p&gt;Email&lt;/p&gt;
                    &lt;input onChange={(e) =&gt; setEmail(e.target.value)} required type="email" name="email" id="email" placeholder="example@mail.com" /&gt;
                &lt;/label&gt;
                &lt;label htmlFor="password"&gt;
                    &lt;p&gt;Password&lt;/p&gt;
                    &lt;input onChange={(e) =&gt; setPassword(e.target.value)} required type="password" name="password" id="password" placeholder="password" /&gt;
                &lt;/label&gt;
                &lt;button type="submit"&gt;Sign up&lt;/button&gt;
            &lt;/form&gt;
        &lt;/div&gt;

    &lt;/div&gt;);
}

export default Page;
</code></pre><h3 id="c-mo-saber-si-hubo-alg-n-cambio-de-autenticaci-n">Cómo saber si hubo algún cambio de autenticación</h3><p>Mientras estás usando la aplicación, siempre vamos a querer saber si el usuario ha iniciado sesión o no. Si el usuario ha iniciado sesión, es posible crear páginas protegidas y mostrarle ciertos contenidos sólo a él. Firebase tiene el método <code>onAuthStateChanged()</code> para poder saber si hubo cambios.</p><p>Para que la información del usuario obtenida con el método mencionado arriba pueda ser usada en todo la aplicación, vamos a usar la API React Context. Crea un carpeta con el nombre <strong>context</strong> en tu directorio <strong>src. </strong>Dentro del directorio <strong>directory</strong>, crea un archivo con el nombre <code>AuthContext.js</code> y agrega el siguiente código:</p><pre><code class="language-jsx">import React from 'react';
import {
    onAuthStateChanged,
    getAuth,
} from 'firebase/auth';
import firebase_app from '@/firebase/config';

const auth = getAuth(firebase_app);

export const AuthContext = React.createContext({});

export const useAuthContext = () =&gt; React.useContext(AuthContext);

export const AuthContextProvider = ({
    children,
}) =&gt; {
    const [user, setUser] = React.useState(null);
    const [loading, setLoading] = React.useState(true);

    React.useEffect(() =&gt; {
        const unsubscribe = onAuthStateChanged(auth, (user) =&gt; {
            if (user) {
                setUser(user);
            } else {
                setUser(null);
            }
            setLoading(false);
        });

        return () =&gt; unsubscribe();
    }, []);

    return (
        &lt;AuthContext.Provider value={{ user }}&gt;
            {loading ? &lt;div&gt;Loading...&lt;/div&gt; : children}
        &lt;/AuthContext.Provider&gt;
    );
};
</code></pre><p>Con el código de arriba simplemente creamos un Provider que devuelve el objeto usuario si el usuario inició sesión. De lo contrario, sólo devolverá <code>null</code>.</p><p>Para poder usar el valor que se le pasó a <code>&lt;AuthContext.Provider&gt;</code> &nbsp;vamos a exportar <code>useAuthContext</code> desde el archivo y así usar el valor de <code>user</code>.</p><p>Antes de poder usar este contexto, necesitamos envolver todos los componentes con <code>AuthContextProvider</code>. Para hacer esto, abrimos el archivo <strong>layout.js</strong> dentro del directorio <strong>src &gt; app</strong> y editamos el código con lo siguiente:</p><pre><code class="language-jsx">'use client'
import './globals.css'
import { AuthContextProvider } from '@/context/AuthContext'

export default function RootLayout({ children }) {
  return (
    &lt;html lang="en"&gt;
      {/*
        &lt;head /&gt; contendrá los componentes devueltos por el padre más cercano
        head.js. Encuentra más información en https://beta.nextjs.org/docs/api-reference/file-conventions/head
      */}
      &lt;head /&gt;
      &lt;body&gt;
        &lt;AuthContextProvider&gt;
          {children}
        &lt;/AuthContextProvider&gt;
      &lt;/body&gt;
    &lt;/html&gt;
  )
}

</code></pre><p>Ahora ya podemos crear páginas protegidas y mostrar contenido específico a usuarios distintos.</p><h3 id="c-mo-crear-p-ginas-protegidas">Cómo crear páginas protegidas</h3><p>Dentro del directorio <strong>app, </strong>crea el directorio <strong>admin &gt; page.js </strong> y agrega el siguiente código:</p><pre><code class="language-jsx">'use client'
import React from "react";
import { useAuthContext } from "@/context/AuthContext";
import { useRouter } from "next/navigation";
function Page() {
    const { user } = useAuthContext()
    const router = useRouter()

    React.useEffect(() =&gt; {
        if (user == null) router.push("/")
    }, [user])

    return (&lt;h1&gt;Solamente los usuarios que iniciaron sesión pueden ver esta página&lt;/h1&gt;);
}

export default Page;
</code></pre><p>Si el usuario es <code>null</code>, lo redireccionamos a la página inicial. De lo contrario, si no es <code>null</code>, le mostramos la página protegida.</p><h2 id="c-mo-comunicarse-con-nuestra-base-de-datos">Cómo comunicarse con nuestra base de datos</h2><p>Ahora que ya lidiamos con la parte de autenticación, podemos enfocarnos en la comunicación con nuestra base de datos. Como base de datos vamos a usar <strong>Firestore.</strong></p><p>Para seguir haciendo todo ordenado, vamos a crear un nuevo directorio llamado <strong>firebase &gt; firestore</strong>. Dentro de este directorio vamos a guardar todo el código relacionado con Firestore.</p><h3 id="c-mo-agregar-documentos-a-firestore">Cómo agregar documentos a Firestore</h3><p>Dentro del directorio <strong>firestore</strong>, crea un archivo con el nombre <code>addData.js</code> y agrega el siguiente código:</p><pre><code class="language-js">import firebase_app from "../config";
import { getFirestore, doc, setDoc } from "firebase/firestore";

const db = getFirestore(firebase_app)
export default async function addData(colllection, id, data) {
    let result = null;
    let error = null;

    try {
        result = await setDoc(doc(db, colllection, id), data, {
            merge: true,
        });
    } catch (e) {
        error = e;
    }

    return { result, error };
}
</code></pre><p>Este código ya debería serte familiar: estamos exportando una función que permite agregar información a nuestra base de datos firestore.</p><p>Ahora ya podemos usar esta función <code>addData()</code> desde cualquier componente para agregar información a nuestra base de datos:</p><pre><code class="language-js">'use client'
import addData from "@/firebase/firestore/addData";

export default function Home() {

  const handleForm = async () =&gt; {
    const data = {
      name: 'John snow',
      house: 'Stark'
    }
    const { result, error } = await addData('users', 'user-id', data)

    if (error) {
      return console.log(error)
    }
  }
  
  return (
    ...
  )
}

</code></pre><h3 id="c-mo-podemos-obtener-un-documento-desde-firestore">Cómo podemos obtener un documento desde Firestore</h3><p>Usando un método similar, podemos obtener un documento desde nuestra base de datos Firebase.</p><p>Crea un archivo <code>getData.js</code> dentro del directorio <strong>Firestore </strong>y agrega el siguiente código:</p><pre><code class="language-js">import firebase_app from "../config";
import { getFirestore, doc, getDoc } from "firebase/firestore";

const db = getFirestore(firebase_app)
export default async function getDoument(collection, id) {
    let docRef = doc(db, collection, id);

    let result = null;
    let error = null;

    try {
        result = await getDoc(docRef);
    } catch (e) {
        error = e;
    }

    return { result, error };
}

</code></pre><p>También podemos usar está función en cualquier componente que quieras.</p><h2 id="conclusi-n">Conclusión</h2><p>En este artículo, aprendimos cómo construir una aplicación <em>fullstack</em> usando Firebase y Next.js 13 para agregar autenticación e interacción con nuestra base de datos.</p><p>¡Feliz codificación!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo construir un editor de código con React que compila y se ejecuta en +40 lenguajes ]]>
                </title>
                <description>
                    <![CDATA[ Una plataforma de ejecución de código te permite escribir código en tu lenguaje de programación favorito y ejecutar ese código en la misma plataforma. Puedes ver perfectamente una salida del programa que has escrito (por ejemplo, un programa de búsqueda binaria escrito en JavaScript). Hoy, vamos a construir una plataforma ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-construir-editor-codigo-con-react-compila-y-ejecuta-en-40-lenguajes/</link>
                <guid isPermaLink="false">651fecd837996103e26c955f</guid>
                
                    <category>
                        <![CDATA[ next.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Wed, 15 Nov 2023 02:18:13 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2023/10/Build-a-React-Code-Editor-That-Compiles-and-Executes-in-10--Languages--1-.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-build-react-based-code-editor/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Build a Code Editor with React that Compiles and Executes in 40+ Languages</a>
      </p><p>Una plataforma de ejecución de código te permite escribir código en tu lenguaje de programación favorito y ejecutar ese código en la misma plataforma.</p><p>Puedes ver perfectamente una salida del programa que has escrito (por ejemplo, un programa de búsqueda binaria escrito en JavaScript).</p><p>Hoy, vamos a construir una plataforma de ejecución de código en línea llamada CodeRush el cual puede compilar y ejecutar código en más de 40+ lenguajes de programación diferentes.</p><h2 id="-qu-estaremos-construyendo"><strong>¿Qué estaremos construyendo?</strong></h2><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screenshot-2022-05-18-at-9.05.14-PM.png" class="kg-image" alt="Screenshot-2022-05-18-at-9.05.14-PM" width="600" height="400" loading="lazy"></figure><p><a href="https://github.com/manuarora700/react-code-editor"></a><a href="https://github.com/manuarora700/react-code-editor">C</a>ódigo Fuente | <a href="https://coderush.vercel.app/">Live Demo</a></p><p>Vamos a construir un rico editor de código que tiene las siguientes características:</p><ul><li>Un Editor de Código (<a href="https://www.npmjs.com/package/monaco-editor">Monaco Editor</a>) que alimenta a VS Code también.</li><li>Puede compilar código en una aplicación web con entrada y salida estándar con soporte a 40 lenguajes de programación.</li><li>Puedes cambiar el tema del editor desde una lista de temas disponibles.</li><li>Puedes obtener información del código ejecutado (tiempo tomado por el código, memoria usada, estado, y más).</li></ul><h2 id="pila-de-tecnolog-as"><strong>Pila de Tecnologías</strong></h2><p>Para el proyecto, vamos a usar la siguiente pila de tecnologías:</p><ul><li><a href="https://reactjs.org/">React.js</a> – Para el front-end</li><li><a href="https://tailwindcss.com/">TailwindCSS</a> – Para los estilos</li><li><a href="https://judge0.com/">Judge0</a> – Para compilar y ejecutar nuestro código</li><li><a href="https://rapidapi.com/">RapidAPI</a> – Para desplegar código Judge0 rápidamente</li><li><a href="https://www.npmjs.com/package/monaco-editor">Monaco Editor</a> – El editor de código que alimenta el proyecto</li></ul><h2 id="estructura-del-proyecto"><strong>Estructura del Proyecto</strong></h2><p>La estructura del proyecto bastante simple y fácil de entender:</p><ul><li><strong><strong>Components</strong></strong>: todos los componentes / porciones de código reusables están aquí (Ejemplo: CodeEditorWindow y Landing)</li><li><strong><strong>hooks</strong></strong>: todos los custom hooks están aquí. (Vamos a usar hooks de presiones de teclas para compilar nuestro código usando los eventos del teclado)</li><li><strong><strong>lib</strong></strong>: todas las funciones de librería están aquí. (Crearemos una función para definir nuestro tema aquí)</li><li><strong><strong>constants</strong></strong>: todas las constantes como <code>languageOptions</code> y <code>customStyles</code> para los desplegables irán aquí.</li><li><strong><strong>utils</strong></strong>: funciones de utilidad generales para ayudar a mantener el código van aquí.</li></ul><h4 id="flujo-de-la-aplicaci-n-"><strong>Flujo de la aplicación:</strong></h4><p>Antes de que indaguemos en el código, entendamos el flujo de la aplicación y cómo deberíamos codificarlo desde cero.</p><ul><li>Un usuario llega a la aplicación web y puede seleccionar sus lenguajes de programación preferidos (por defecto es JavaScript).</li><li>Una vez que el usuario termina de escribir su código, pueden compilar su código y ver la salida/resultados en la ventana de salida.</li><li>Verán éxito o fracaso de sus porciones de código. Todo es visible en la ventana de salida del código.</li><li>El usuario puede agregar entradas personalizadas para sus porciones de código, y el juez (nuestro compilador en línea) tomará en cuenta la entrada personalizada la cual el usuario provee.</li><li>El usuario puede ver detalles relevantes del código que fue ejecutado (Ejemplo: tomó 5ms para que el código se compile y se ejecute, 2024 kb de memoria fue usado, y el estado de tiempo de ejecución fue exitoso).</li></ul><p>Ahora que estamos un poco más familiar con la estructura de carpetas y el flujo de la aplicación, indaguemos en el código y entendamos cómo funciona todo.</p><h2 id="c-mo-construir-el-componente-editor-de-c-digo"><strong>Cómo construir el componente editor de código</strong></h2><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screenshot-2022-05-18-at-9.26.48-PM.png" class="kg-image" alt="Screenshot-2022-05-18-at-9.26.48-PM" width="600" height="400" loading="lazy"></figure><p>Primeramente, el componente editor de código está formado por el Editor Monaco, el cual es un paquete de NPM que podemos usar y personalizar.</p><pre><code class="language-javascript">// CodeEditorWindow.js

import React, { useState } from "react";

import Editor from "@monaco-editor/react";

const CodeEditorWindow = ({ onChange, language, code, theme }) =&gt; {
  const [value, setValue] = useState(code || "");

  const handleEditorChange = (value) =&gt; {
    setValue(value);
    onChange("code", value);
  };

  return (
    &lt;div className="overlay rounded-md overflow-hidden w-full h-full shadow-4xl"&gt;
      &lt;Editor
        height="85vh"
        width={`100%`}
        language={language || "javascript"}
        value={value}
        theme={theme}
        defaultValue="// some comment"
        onChange={handleEditorChange}
      /&gt;
    &lt;/div&gt;
  );
};
export default CodeEditorWindow;
</code></pre><p>El componente <code>Editor</code> viene del paquete <code>@monaco-editor/react</code> que nos permite poner en marcha un editor de código con una altura de <code>85vh</code> como se especifica.</p><p>El componente <code>Editor</code> toma un par de props:</p><ul><li><code>language</code>: el lenguaje por el cual necesitamos resaltado de sintaxis e intellisense</li><li><code>theme</code>: Los colores y fondos de la porción del código (lo configuraremos en la última parte del tutorial)</li><li><code>value</code>: El valor del código actual que va en el editor de código</li><li><code>onChange</code>: esto se dispara cuando el valor en el editor de código cambia. Necesitamos guardar el valor modificado en el estado así podemos, más adelante, llamar a la API <code>Judge0</code> para compilarlo</li></ul><p>El editor recibe los props <code>onChange</code>, <code>language</code>, <code>code</code>, y <code>theme</code> de su componente padre, el cual es <code>Landing.js</code>. Cada vez que el <code>value</code> en el editor de código cambia, llamamos al manejador <code>onChange</code> que está presente en el componente padre <code>Landing</code>.</p><h2 id="c-mo-construir-el-componente-landing"><strong>Cómo construir el componente landing</strong></h2><p>El componente landing consiste mayormente de 3 partes:</p><ul><li>El <code>Actions Bar</code> el cual tiene los componentes <code>Languages</code> y los desplegables <code>Themes</code>.</li><li>El componente <code>Code Editor Window</code></li><li>Los componentes <code>Output and Custom Input</code> </li></ul><pre><code class="language-javascript">// Landing.js

import React, { useEffect, useState } from "react";
import CodeEditorWindow from "./CodeEditorWindow";
import axios from "axios";
import { classnames } from "../utils/general";
import { languageOptions } from "../constants/languageOptions";

import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";

import { defineTheme } from "../lib/defineTheme";
import useKeyPress from "../hooks/useKeyPress";
import Footer from "./Footer";
import OutputWindow from "./OutputWindow";
import CustomInput from "./CustomInput";
import OutputDetails from "./OutputDetails";
import ThemeDropdown from "./ThemeDropdown";
import LanguagesDropdown from "./LanguagesDropdown";

const javascriptDefault = `// algún comentario`;

const Landing = () =&gt; {
  const [code, setCode] = useState(javascriptDefault);
  const [customInput, setCustomInput] = useState("");
  const [outputDetails, setOutputDetails] = useState(null);
  const [processing, setProcessing] = useState(null);
  const [theme, setTheme] = useState("cobalt");
  const [language, setLanguage] = useState(languageOptions[0]);

  const enterPress = useKeyPress("Enter");
  const ctrlPress = useKeyPress("Control");

  const onSelectChange = (sl) =&gt; {
    console.log("Opción seleccionada...", sl);
    setLanguage(sl);
  };

  useEffect(() =&gt; {
    if (enterPress &amp;&amp; ctrlPress) {
      console.log("enterPress", enterPress);
      console.log("ctrlPress", ctrlPress);
      handleCompile();
    }
  }, [ctrlPress, enterPress]);
  const onChange = (action, data) =&gt; {
    switch (action) {
      case "code": {
        setCode(data);
        break;
      }
      default: {
        console.warn("caso no manejado!", action, data);
      }
    }
  };
  const handleCompile = () =&gt; {
    // Haremos la implementación luego en el código
  };

  const checkStatus = async (token) =&gt; {
    // Haremos la implementación luego en el código
  };

  function handleThemeChange(th) {
    // Haremos la implementación luego en el código
  }
  useEffect(() =&gt; {
    defineTheme("oceanic-next").then((_) =&gt;
      setTheme({ value: "oceanic-next", label: "Oceanic Next" })
    );
  }, []);

  const showSuccessToast = (msg) =&gt; {
    toast.success(msg || `Compilado satisfactoriamente!`, {
      position: "top-right",
      autoClose: 1000,
      hideProgressBar: false,
      closeOnClick: true,
      pauseOnHover: true,
      draggable: true,
      progress: undefined,
    });
  };
  const showErrorToast = (msg) =&gt; {
    toast.error(msg || `¡Algo salió mal! Por favor intenta otra vez.`, {
      position: "top-right",
      autoClose: 1000,
      hideProgressBar: false,
      closeOnClick: true,
      pauseOnHover: true,
      draggable: true,
      progress: undefined,
    });
  };

  return (
    &lt;&gt;
      &lt;ToastContainer
        position="top-right"
        autoClose={2000}
        hideProgressBar={false}
        newestOnTop={false}
        closeOnClick
        rtl={false}
        pauseOnFocusLoss
        draggable
        pauseOnHover
      /&gt;
      &lt;div className="h-4 w-full bg-gradient-to-r from-pink-500 via-red-500 to-yellow-500"&gt;&lt;/div&gt;
      &lt;div className="flex flex-row"&gt;
        &lt;div className="px-4 py-2"&gt;
          &lt;LanguagesDropdown onSelectChange={onSelectChange} /&gt;
        &lt;/div&gt;
        &lt;div className="px-4 py-2"&gt;
          &lt;ThemeDropdown handleThemeChange={handleThemeChange} theme={theme} /&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div className="flex flex-row space-x-4 items-start px-4 py-4"&gt;
        &lt;div className="flex flex-col w-full h-full justify-start items-end"&gt;
          &lt;CodeEditorWindow
            code={code}
            onChange={onChange}
            language={language?.value}
            theme={theme.value}
          /&gt;
        &lt;/div&gt;

        &lt;div className="right-container flex flex-shrink-0 w-[30%] flex-col"&gt;
          &lt;OutputWindow outputDetails={outputDetails} /&gt;
          &lt;div className="flex flex-col items-end"&gt;
            &lt;CustomInput
              customInput={customInput}
              setCustomInput={setCustomInput}
            /&gt;
            &lt;button
              onClick={handleCompile}
              disabled={!code}
              className={classnames(
                "mt-4 border-2 border-black z-10 rounded-md shadow-[5px_5px_0px_0px_rgba(0,0,0)] px-4 py-2 hover:shadow transition duration-200 bg-white flex-shrink-0",
                !code ? "opacity-50" : ""
              )}
            &gt;
              {processing ? "Procesando..." : "Compilar y ejecutar"}
            &lt;/button&gt;
          &lt;/div&gt;
          {outputDetails &amp;&amp; &lt;OutputDetails outputDetails={outputDetails} /&gt;}
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;Footer /&gt;
    &lt;/&gt;
  );
};
export default Landing;
</code></pre><p>Entendamos la estructura básica de la página landing en más detalle.</p><h3 id="componente-codeeditorwindow"><strong>Componente CodeEditorWindow </strong></h3><p>Como lo vimos antes, el componente CodeEditorWindow tomará en cuenta el código (el cual sigue cambiando) y un método <code>onChange</code> el cual mantendrá un registro de los cambios del código.</p><pre><code class="language-javascript">// implementación del método onChange

 const onChange = (action, data) =&gt; {
    switch (action) {
      case "code": {
        setCode(data);
        break;
      }
      default: {
        console.warn("caso no manejado!", action, data);
      }
    }
  };</code></pre><p>Simplemente establecemos el estado del <code>code</code> y hacemos un registro de los cambios. </p><p>El componente <code>CodeEditorWindow</code> también toma en cuenta la prop <code>language</code>, el cual es el actual lenguaje seleccionado que necesitamos para el resaltado de sintaxis y para intellisense. </p><p>He creado un arreglo <code>languageOptions</code> el cual mantiene un registro de las props de lenguajes aceptados por el Editor Monaco y también maneja la compilación (mantenemos un registro del <code>languageId</code> que es aceptado por las APIs de <code>judge0</code>). </p><pre><code class="language-javascript">// constants/languageOptions.js

export const languageOptions = [
  {
    id: 63,
    name: "JavaScript (Node.js 12.14.0)",
    label: "JavaScript (Node.js 12.14.0)",
    value: "javascript",
  },
  {
    id: 45,
    name: "Assembly (NASM 2.14.02)",
    label: "Assembly (NASM 2.14.02)",
    value: "assembly",
  },
    ...
    ...
    ...
    ...
    ...
    ...
    
  {
    id: 84,
    name: "Visual Basic.Net (vbnc 0.0.0.5943)",
    label: "Visual Basic.Net (vbnc 0.0.0.5943)",
    value: "vbnet",
  },
];</code></pre><p>Cada objeto <code>languageOptions</code> contiene las claves <code>id</code>, <code>name</code>, <code>label</code> y <code>value</code>. El arreglo entero de <code>languageOptions</code> puede ser tomado y puesto dentro del despegable y suplirlos como opciones.</p><p>Cuando el estado del despegable cambie, el método <code>onSelectChange</code> mantiene un registro del <code>id</code> seleccionado y cambia el estado apropiadamente.</p><h3 id="componente-languagedropdown"><strong>Componente LanguageDropdown</strong></h3><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screenshot-2022-05-19-at-10.42.43-PM.png" class="kg-image" alt="Screenshot-2022-05-19-at-10.42.43-PM" width="600" height="400" loading="lazy"></figure><pre><code class="language-javascript">// LanguageDropdown.js

import React from "react";
import Select from "react-select";
import { customStyles } from "../constants/customStyles";
import { languageOptions } from "../constants/languageOptions";

const LanguagesDropdown = ({ onSelectChange }) =&gt; {
  return (
    &lt;Select
      placeholder={`Filter By Category`}
      options={languageOptions}
      styles={customStyles}
      defaultValue={languageOptions[0]}
      onChange={(selectedOption) =&gt; onSelectChange(selectedOption)}
    /&gt;
  );
};

export default LanguagesDropdown;

</code></pre><p>Para el despegable, vamos a usar el paquete <a href="https://react-select.com/">react-select</a> que se encarga de los despegables y sus manejadores de cambio.</p><p>React select toma <code>defaultValue</code> y <code>options</code> como los parámetros mayores. <code>options</code> es un arreglo (y vamos a pasar <code>languageOptions</code> aquí) que automáticamente muestra todos los valores relevantes del despegable. </p><p>La prop <code>defaultValue</code> es el valor por defecto que es provisto al componente. Vamos a mantener a JavaScript como el lenguaje por defecto (el cual es el primero de nuestro arreglo de lenguajes).</p><p>Cuando sea que el usuario cambie el lenguaje, cambiamos el lenguaje con el callback <code>onSelectChange</code>:</p><pre><code class="language-javascript">const onSelectChange = (sl) =&gt; {
    setLanguage(sl);
};</code></pre><h3 id="componente-themedropdown"><strong>Componente ThemeDropdown</strong></h3><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screenshot-2022-05-19-at-10.42.43-PM-1.png" class="kg-image" alt="Screenshot-2022-05-19-at-10.42.43-PM-1" width="600" height="400" loading="lazy"></figure><p>El componente <code>ThemeDropdown</code> en realidad es muy similar al componente <code>LanguageDropdown</code> (con la UI y el paquete react-select).</p><pre><code class="language-javascript">// ThemeDropdown.js

import React from "react";
import Select from "react-select";
import monacoThemes from "monaco-themes/themes/themelist";
import { customStyles } from "../constants/customStyles";

const ThemeDropdown = ({ handleThemeChange, theme }) =&gt; {
  return (
    &lt;Select
      placeholder={`Select Theme`}
      // options={languageOptions}
      options={Object.entries(monacoThemes).map(([themeId, themeName]) =&gt; ({
        label: themeName,
        value: themeId,
        key: themeId,
      }))}
      value={theme}
      styles={customStyles}
      onChange={handleThemeChange}
    /&gt;
  );
};

export default ThemeDropdown;
</code></pre><p>Aquí, vamos a usar el paquete <code>monacoThemes</code> para que nos ayude con los diferentes temas preciosos que están disponibles en el internet para el Editor Monaco.</p><p>Tenemos una lista de temas disponibles a nuestra disposición.</p><pre><code class="language-javascript">// lib/defineTheme.js

import { loader } from "@monaco-editor/react";

const monacoThemes = {
  active4d: "Active4D",
  "all-hallows-eve": "All Hallows Eve",
  amy: "Amy",
  "birds-of-paradise": "Birds of Paradise",
  blackboard: "Blackboard",
  "brilliance-black": "Brilliance Black",
  "brilliance-dull": "Brilliance Dull",
  "chrome-devtools": "Chrome DevTools",
  "clouds-midnight": "Clouds Midnight",
  clouds: "Clouds",
  cobalt: "Cobalt",
  dawn: "Dawn",
  dreamweaver: "Dreamweaver",
  eiffel: "Eiffel",
  "espresso-libre": "Espresso Libre",
  github: "GitHub",
  idle: "IDLE",
  katzenmilch: "Katzenmilch",
  "kuroir-theme": "Kuroir Theme",
  lazy: "LAZY",
  "magicwb--amiga-": "MagicWB (Amiga)",
  "merbivore-soft": "Merbivore Soft",
  merbivore: "Merbivore",
  "monokai-bright": "Monokai Bright",
  monokai: "Monokai",
  "night-owl": "Night Owl",
  "oceanic-next": "Oceanic Next",
  "pastels-on-dark": "Pastels on Dark",
  "slush-and-poppies": "Slush and Poppies",
  "solarized-dark": "Solarized-dark",
  "solarized-light": "Solarized-light",
  spacecadet: "SpaceCadet",
  sunburst: "Sunburst",
  "textmate--mac-classic-": "Textmate (Mac Classic)",
  "tomorrow-night-blue": "Tomorrow-Night-Blue",
  "tomorrow-night-bright": "Tomorrow-Night-Bright",
  "tomorrow-night-eighties": "Tomorrow-Night-Eighties",
  "tomorrow-night": "Tomorrow-Night",
  tomorrow: "Tomorrow",
  twilight: "Twilight",
  "upstream-sunburst": "Upstream Sunburst",
  "vibrant-ink": "Vibrant Ink",
  "xcode-default": "Xcode_default",
  zenburnesque: "Zenburnesque",
  iplastic: "iPlastic",
  idlefingers: "idleFingers",
  krtheme: "krTheme",
  monoindustrial: "monoindustrial",
};

const defineTheme = (theme) =&gt; {
  return new Promise((res) =&gt; {
    Promise.all([
      loader.init(),
      import(`monaco-themes/themes/${monacoThemes[theme]}.json`),
    ]).then(([monaco, themeData]) =&gt; {
      monaco.editor.defineTheme(theme, themeData);
      res();
    });
  });
};

export { defineTheme };</code></pre><p>El paquete <code>monaco-themes</code> nos provee con un montón de temas que podemos usar para definir cómo va a lucir nuestro editor de código. </p><p>La función <code>defineTheme</code> se encarga de los diferentes temas de donde el usuario podría seleccionar. La función <code>defineTheme</code> regresa una promesa que en realidad establece el tema del editor Monaco usando la acción <code>monaco.editor.defineTheme(theme, themeData)</code>. Esta línea de código es responsable en sí de cambiar los temas dentro de una ventana de código del Editor Monaco. </p><p>La función <code>defineTheme</code> es llamada con la ayuda del callback <code>onChange</code> que vimos anteriormente en el componente <code>ThemeDropdown.js</code>.</p><pre><code class="language-javascript">// Landing.js - función handleThemeChange()

function handleThemeChange(th) {
    const theme = th;
    console.log("theme...", theme);

    if (["light", "vs-dark"].includes(theme.value)) {
      setTheme(theme);
    } else {
      defineTheme(theme.value).then((_) =&gt; setTheme(theme));
    }
  }
  </code></pre><p>La función <code>handleThemeChange()</code> verifica si el tema es <code>light</code> o <code>dark</code>. Estos temas por defecto están disponibles en el componente <code>MonacoEditor</code> y no necesitamos llamar al método <code>defineTheme()</code> para esto.</p><p>Si no, simplemente llamamos al componente <code>defineTheme()</code> y ponemos el estado del tema seleccionado. </p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screenshot-2022-05-19-at-10.46.34-PM.png" class="kg-image" alt="Screenshot-2022-05-19-at-10.46.34-PM" width="600" height="400" loading="lazy"></figure><h2 id="c-mo-compilar-el-c-digo-con-judge0"><strong>Cómo compilar el código con Judge0</strong></h2><p>Adentrémonos a la parte sustanciosa de la aplicación – el cual es compilar el código con diferentes lenguajes.</p><p>Para compilar nuestro código, vamos a usar Judge0. Judge0 es un sistema simple de ejecución de código, de código abierto, con el que podemos interactuar.<br><br>Podemos hacer una simple llamada API con algunos parámetros (código de fuente, ID de lenguaje) y obtenemos la salida como una respuesta.</p><p>Configuremos Judge0 y sigamos con los siguientes pasos:</p><ul><li>Dirígete a <a href="https://judge0.com/">Judge0</a> y elige el Plan Básico</li><li>Judge0 está hospedado en realidad en <a href="https://rapidapi.com/">RapidAPI</a>. Ve y suscríbete al plan básico.</li><li>Una vez que estés suscrito, puedes copiar el <code>RAPIDAPI_HOST</code> y la <code>RAPIDAPI_KEY</code> los cuales son necesarios para hacer las llamadas a la API para nuestro sistema de ejecución de código.</li></ul><p>El panel luce algo así:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Untitled-design.png" class="kg-image" alt="Untitled-design" width="600" height="400" loading="lazy"></figure><p>Los parámetros <code>X-RapidAPI-Host</code> y <code>X-RapidAPI-Key</code> serán requeridos para nuestras llamadas a la API. Guárdalo para un uso futuro en el archivo <code>.env</code> así:</p><pre><code class="language-env">REACT_APP_RAPID_API_HOST = YOUR_HOST_URL
REACT_APP_RAPID_API_KEY = YOUR_SECRET_KEY
REACT_APP_RAPID_API_URL = YOUR_SUBMISSIONS_URL</code></pre><p>Es importante en React que inicialicemos nuestras variables de entorno con el prefijo <code>REACT_APP</code>.</p><p>La <code>SUBMISSIONS_URL</code> es la URL que vamos a usar. Básicamente consiste de tu <code>host</code> , seguido de una ruta <code>/submission</code>.<br><br>Por ejemplo: <code>https://judge0-ce.p.rapidapi.com/submissions</code> será la URL <code>submissions</code> en nuestro caso.</p><p>Una vez que tenemos las variables configuradas correctamente podemos continuar adelante y manejar la lógica de <code>compilation</code>.</p><h4 id="flujo-de-compilaci-n-y-l-gica"><strong>Flujo de Compilación y Lógica</strong></h4><p>El flujo de compilación es como sigue:</p><ul><li>Cuando se hace clic en el botón <code>Compile and Execute</code>, el método <code>handleCompile()</code> es llamado.</li><li>La función <code>handleCompile()</code> llama a nuestro backend <code>Judge0 RapidAPI</code> en el URL <code>submissions</code> con <code>languageId</code> , <code>source_code</code>, y <code>stdin</code> (customInput en nuestro caso) como parámetros del cuerpo.</li><li>Los <code>options</code> también toman el <code>host</code> y el <code>secret</code> como encabezados.</li><li><code>base64_encoded</code> y <code>fields</code> son parámetros opcionales que pueden ser pasados.</li><li>La petición POST <code>submission</code> registra nuestra petición en el servidor y crea un proceso. La respuesta de la petición <code>post</code> es un <code>token</code> que puede ser usado más adelante para verificar el estado de nuestra ejecución. (Hay varios estados – Procesando, Aceptado, Límite de Tiempo Excedido, Excepciones de Tiempo de Ejecución y más.)</li><li>Una vez que nuestros resultados son regresados, podemos verificar condicionalmente si los resultados son exitosos o no y mostrar los resultados en nuestras pantallas.</li></ul><p>Adentrémonos en el código y entendamos el método <code>handleCompile()</code>.</p><pre><code class="language-javascript">const handleCompile = () =&gt; {
    setProcessing(true);
    const formData = {
      language_id: language.id,
      // codifica el código fuente en base64
      source_code: btoa(code),
      stdin: btoa(customInput),
    };
    const options = {
      method: "POST",
      url: process.env.REACT_APP_RAPID_API_URL,
      params: { base64_encoded: "true", fields: "*" },
      headers: {
        "content-type": "application/json",
        "Content-Type": "application/json",
        "X-RapidAPI-Host": process.env.REACT_APP_RAPID_API_HOST,
        "X-RapidAPI-Key": process.env.REACT_APP_RAPID_API_KEY,
      },
      data: formData,
    };

    axios
      .request(options)
      .then(function (response) {
        console.log("res.data", response.data);
        const token = response.data.token;
        checkStatus(token);
      })
      .catch((err) =&gt; {
        let error = err.response ? err.response.data : err;
        setProcessing(false);
        console.log(error);
      });
  };</code></pre><p>Como se ve arriba, el método <code>handleCompile()</code> toma <code>languageId</code>, <code>source_code</code> y <code>stdin</code>. Nota el <code>btoa</code> antes de <code>source_code</code> y <code>stdin</code>. Esto es para codificar nuestras cadenas a base64 ya que estamos usando <code>base64_encoded: true</code> en nuestros parámetros para la API.</p><p>Cuando hay una respuesta exitosa y tenemos un <code>token</code>, llamamos al método <code>checkStatus()</code> para sondear la ruta <code>/submissions/${token}</code>.</p><pre><code class="language-javascript">const checkStatus = async (token) =&gt; {
    const options = {
      method: "GET",
      url: process.env.REACT_APP_RAPID_API_URL + "/" + token,
      params: { base64_encoded: "true", fields: "*" },
      headers: {
        "X-RapidAPI-Host": process.env.REACT_APP_RAPID_API_HOST,
        "X-RapidAPI-Key": process.env.REACT_APP_RAPID_API_KEY,
      },
    };
    try {
      let response = await axios.request(options);
      let statusId = response.data.status?.id;

      // Procesado - tenemos un resultado
      if (statusId === 1 || statusId === 2) {
        // todavía procesando
        setTimeout(() =&gt; {
          checkStatus(token)
        }, 2000)
        return
      } else {
        setProcessing(false)
        setOutputDetails(response.data)
        showSuccessToast(`Compilado satisfactoriamente!`)
        console.log('response.data', response.data)
        return
      }
    } catch (err) {
      console.log("err", err);
      setProcessing(false);
      showErrorToast();
    }
  };</code></pre><p>Para obtener los resultados de nuestro código que enviamos anteriormente, necesitamos sondear a la API <code>submissions</code> con un <code>token</code> que recibimos como respuesta.</p><p>Como vimos arriba, vamos a hacer una petición GET al endpoint. Una vez que tengamos una respuesta, vamos a verificar &nbsp;<code>statusId === 1 || statusId === 2</code>. Pero ¿Qué significa?<br><br>Tenemos un total de <code>14</code> estados asociados con cualquier porción de código que enviamos a la API, y esos son:</p><pre><code class="language-javascript">export const statuses = [
  {
    id: 1,
    description: "In Queue",
  },
  {
    id: 2,
    description: "Processing",
  },
  {
    id: 3,
    description: "Accepted",
  },
  {
    id: 4,
    description: "Wrong Answer",
  },
  {
    id: 5,
    description: "Time Limit Exceeded",
  },
  {
    id: 6,
    description: "Compilation Error",
  },
  {
    id: 7,
    description: "Runtime Error (SIGSEGV)",
  },
  {
    id: 8,
    description: "Runtime Error (SIGXFSZ)",
  },
  {
    id: 9,
    description: "Runtime Error (SIGFPE)",
  },
  {
    id: 10,
    description: "Runtime Error (SIGABRT)",
  },
  {
    id: 11,
    description: "Runtime Error (NZEC)",
  },
  {
    id: 12,
    description: "Runtime Error (Other)",
  },
  {
    id: 13,
    description: "Internal Error",
  },
  {
    id: 14,
    description: "Exec Format Error",
  },
];
</code></pre><p>Así que, si &nbsp;<code>statusId ===1</code> o <code>statusId ===2</code> significa que nuestro código todavía está procesando y necesitamos llamar a la API de nuevo para verificar si obtenemos algún resultado o no.</p><p>Por esto, tenemos un <code>setTimeout()</code> en la condición <code>if</code> que llama a la función <code>checkStatus()</code> otra vez, el cual internamente llama a la API nuevamente y verifica el estado.<br><br>Si el estado es algo más que <code>2</code> o <code>3</code> significa que la ejecución de nuestro código ha sido completada y tenemos un resultado. Sea un código <code>successfully compiled</code> o un código <code>Time Limit Exceeded</code> – o tal vez sea un <code>Runtime Exception</code>. El <code>statusId</code> representa cada escenario y podemos replicarlos también.<br><br>Por ejemplo, <code>while(true)</code> nos dará un error <code>time limit exceeded</code>:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screenshot-2022-05-20-at-1.33.08-AM.png" class="kg-image" alt="Screenshot-2022-05-20-at-1.33.08-AM" width="600" height="400" loading="lazy"></figure><p>O si hacemos un error en la sintaxis, obtendremos un error de compilación:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screenshot-2022-05-20-at-1.34.42-AM.png" class="kg-image" alt="Screenshot-2022-05-20-at-1.34.42-AM" width="600" height="400" loading="lazy"></figure><p>De cualquier forma, vamos a obtener un resultado. Y vamos a almacenar este resultado en nuestro estado <code>outputDetails</code>. Esto es para asegurar que tenemos algo para mostrar en la parte derecha de la pantalla (el cual es la Pantalla de Salida).</p><h3 id="componente-output-window"><strong>Componente Output Window</strong></h3><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screenshot-2022-05-20-at-1.37.39-AM.png" class="kg-image" alt="Screenshot-2022-05-20-at-1.37.39-AM" width="600" height="400" loading="lazy"></figure><pre><code class="language-javascript">import React from "react";

const OutputWindow = ({ outputDetails }) =&gt; {
  const getOutput = () =&gt; {
    let statusId = outputDetails?.status?.id;

    if (statusId === 6) {
      // error de compilación
      return (
        &lt;pre className="px-2 py-1 font-normal text-xs text-red-500"&gt;
          {atob(outputDetails?.compile_output)}
        &lt;/pre&gt;
      );
    } else if (statusId === 3) {
      return (
        &lt;pre className="px-2 py-1 font-normal text-xs text-green-500"&gt;
          {atob(outputDetails.stdout) !== null
            ? `${atob(outputDetails.stdout)}`
            : null}
        &lt;/pre&gt;
      );
    } else if (statusId === 5) {
      return (
        &lt;pre className="px-2 py-1 font-normal text-xs text-red-500"&gt;
          {`Time Limit Exceeded`}
        &lt;/pre&gt;
      );
    } else {
      return (
        &lt;pre className="px-2 py-1 font-normal text-xs text-red-500"&gt;
          {atob(outputDetails?.stderr)}
        &lt;/pre&gt;
      );
    }
  };
  return (
    &lt;&gt;
      &lt;h1 className="font-bold text-xl bg-clip-text text-transparent bg-gradient-to-r from-slate-900 to-slate-700 mb-2"&gt;
        Output
      &lt;/h1&gt;
      &lt;div className="w-full h-56 bg-[#1e293b] rounded-md text-white font-normal text-sm overflow-y-auto"&gt;
        {outputDetails ? &lt;&gt;{getOutput()}&lt;/&gt; : null}
      &lt;/div&gt;
    &lt;/&gt;
  );
};

export default OutputWindow;
</code></pre><p>Este es un componente directo que solamente muestra los escenarios apropiados de éxito o falla.</p><p>El método <code>getOutput()</code> determinará como se verá el color del texto y qué debería ser impreso. </p><ul><li>Si <code>statusId</code> es <code>6</code> – Tenemos un error de compilación. Para ello, la API regresa un <code>compile_output</code> que puede ser usado para mostrar el error.</li><li>Si <code>statusId</code> es <code>3</code> – Tenemos un escenario de éxito, el cual es <code>Accepted</code>. La API regresa un <code>stdout</code> el cual significa Salida Estándar (Standard Output). Esto es usado para mostrar los datos el cual es devuelto (returned) del código que suplimos a la API.</li><li>Si <code>statusId</code> es <code>5</code> – Tenemos un error Time Limit Exceeded. Simplemente mostramos que hay una condición de bucle infinito en el código o si excede el tiempo estándar de <code>5</code> segundos para la ejecución del código.</li><li>Para cualquier otro estado, vamos a obtener un objeto estándar <code>stderr</code> que podemos usar para mostrar los errores.</li><li>Nota el método <code>atob()</code> en uso. Esto es porque obtenemos la salida como una cadena base64. Para decodificarlo, usamos el método <code>atob()</code>.</li></ul><p>Aquí hay un escenario de éxito de un programa <code>Binary Search</code> en JavaScript:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screenshot-2022-05-20-at-1.42.55-AM.png" class="kg-image" alt="Screenshot-2022-05-20-at-1.42.55-AM" width="600" height="400" loading="lazy"></figure><h3 id="componente-output-details"><strong>Componente Output Details</strong></h3><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screenshot-2022-05-20-at-1.44.01-AM.png" class="kg-image" alt="Screenshot-2022-05-20-at-1.44.01-AM" width="600" height="400" loading="lazy"></figure><p>El componente <code>OutputDetails</code> es un "mapeador" simple para mostrar los detalles asociados con un pedazo de código que inicialmente compilamos. Los datos ya están puestos en la variable de estado <code>outputDetails</code>.</p><pre><code class="language-javascript">import React from "react";

const OutputDetails = ({ outputDetails }) =&gt; {
  return (
    &lt;div className="metrics-container mt-4 flex flex-col space-y-3"&gt;
      &lt;p className="text-sm"&gt;
        Status:{" "}
        &lt;span className="font-semibold px-2 py-1 rounded-md bg-gray-100"&gt;
          {outputDetails?.status?.description}
        &lt;/span&gt;
      &lt;/p&gt;
      &lt;p className="text-sm"&gt;
        Memory:{" "}
        &lt;span className="font-semibold px-2 py-1 rounded-md bg-gray-100"&gt;
          {outputDetails?.memory}
        &lt;/span&gt;
      &lt;/p&gt;
      &lt;p className="text-sm"&gt;
        Time:{" "}
        &lt;span className="font-semibold px-2 py-1 rounded-md bg-gray-100"&gt;
          {outputDetails?.time}
        &lt;/span&gt;
      &lt;/p&gt;
    &lt;/div&gt;
  );
};

export default OutputDetails;
</code></pre><p>Los atributos <code>time</code>, <code>memory</code> y <code>status.description</code> todos son recibidos de la respuesta de la API los cuales luego son almacenados en <code>outputDetails</code> y mostrados.</p><h3 id="eventos-del-teclado"><strong>Eventos del Teclado</strong></h3><p>Lo último en la aplicación es usar <code>ctrl+enter</code> para compilar nuestro código. Para esto, vamos a crear un hook personalizado (porque son increíbles y más limpios) para escuchar varios eventos del teclado en nuestra aplicación web.</p><pre><code class="language-javascript">// useKeyPress.js

import React, { useState } from "react";

const useKeyPress = function (targetKey) {
  const [keyPressed, setKeyPressed] = useState(false);

  function downHandler({ key }) {
    if (key === targetKey) {
      setKeyPressed(true);
    }
  }

  const upHandler = ({ key }) =&gt; {
    if (key === targetKey) {
      setKeyPressed(false);
    }
  };

  React.useEffect(() =&gt; {
    document.addEventListener("keydown", downHandler);
    document.addEventListener("keyup", upHandler);

    return () =&gt; {
      document.removeEventListener("keydown", downHandler);
      document.removeEventListener("keyup", upHandler);
    };
  });

  return keyPressed;
};

export default useKeyPress;
</code></pre><pre><code class="language-javascript">// Landing.js

...
...
...
const Landing = () =&gt; {
    ...
    ...
      const enterPress = useKeyPress("Enter");
      const ctrlPress = useKeyPress("Control");
   ...
   ...
}
</code></pre><p>Aquí, estamos usando los eventos nativos de JavaScript <code>Event Listeners</code> para escuchar nuestra clave <code>target</code> el cual vamos a usar.</p><p>El <code>Hook</code> escucha a los eventos <code>keydown</code> y <code>keyup</code>. Inicializamos nuestro hook con una clave target de <code>Enter</code> y <code>Control</code>.<br><br>Ya que estamos verificando si el <code>targetKey === key</code> y estableciendo <code>keyPressed</code> respectivamente, podemos usar el booleano <code>keyPressed</code> regresado, el cual será <code>true</code> o <code>false</code>.<br><br>Ahora, podemos escuchar a estos eventos en el hook <code>useEffect</code> para asegurarnos de que ambos fueron presionados al mismo tiempo:</p><pre><code class="language-javascript">useEffect(() =&gt; {
    if (enterPress &amp;&amp; ctrlPress) {
      console.log("enterPress", enterPress);
      console.log("ctrlPress", ctrlPress);
      handleCompile();
    }
  }, [ctrlPress, enterPress]);</code></pre><p>Así que cuando sea que el usuario apriete <code>control</code> y <code>enter</code> uno después del otro o al mismo tiempo, el método <code>handleCompile()</code> será llamado.</p><h2 id="algunas-cosas-a-tener-en-mente"><strong>Algunas cosas a tener en mente</strong></h2><p>Este fue un proyecto divertido con el cual trabajar. Pero el plan básico de Judeg0 tiene algunas limitaciones, a saber 100 peticiones por día.</p><p>Para solucionar esto, podrías crear tu propio servidor / droplet (en Digital Ocean) y auto alojar el proyecto de código abierto (tienen una documentación excelente para eso).</p><h2 id="conclusi-n"><strong>Conclusión</strong></h2><p>Al final, tenemos:</p><ul><li>Un editor de código que puede compilar a más de 40+ lenguajes</li><li>Un conmutador de temas para cambiar la apariencia de nuestro editor de código</li><li>Interactuar y Alojar APIs en RapidAPI</li><li>Usar eventos del teclado en React usando hooks personalizados</li><li>¡Mucha y mucha diversión! ;)</li></ul><p>Finalmente, si quieres profundizar en el proyecto, aquí hay algunas de las características que podrías considerar para implementar:</p><ul><li>Módulos de inicio de sesión y registro – así puedes guardar tu código en tu propio panel personal.</li><li>Una forma de compartir el código con otras personas por internet.</li><li>Página de perfil y personalizaciones.</li><li>Programación en pareja en una porción de código único usando programación de sockets y transformaciones operacionales.</li><li>Marcar porciones de código como favoritos.</li><li>Panel personalizado de tus pedazos de código (que están guardados) - justo como CodePen.</li></ul><p>Me encantó realmente codificar esta aplicación desde cero, y TailwindCSS es mi recurso favorito absoluto para estilar mis aplicaciones.</p><p>Si este artículo te fue de utilidad, deja un ⭐️ en el <a href="https://github.com/manuarora700/react-code-editor">repositorio de </a><a href="https://github.com/manuarora700/react-code-editor">GitHub</a>.<br>Si tienes alguna pregunta, por favor contáctame por mi <a href="https://twitter.com/mannupaaji">Twitter</a> y/o <a href="https://manuarora.in/"></a><a href="https://manuarora.in/">S</a>itio web y me encantaría ayudarte.</p><p><a href="https://github.com/manuarora700/react-code-editor"></a><a href="https://github.com/manuarora700/react-code-editor">C</a>ódigo Fuente | <a href="https://coderush.vercel.app/"></a><a href="https://coderush.vercel.app/">Demo</a> en vivo</p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
