<?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[ Jorge Sierra - 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[ Jorge Sierra - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/espanol/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 15 Jun 2026 05:22:19 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/espanol/news/author/jorge_sierra/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Cómo configurar el inicio de sesión de Google en React Native y Firebase ]]>
                </title>
                <description>
                    <![CDATA[ El inicio de sesión de Google es una excelente función de inicio de sesión para ofrecer a los usuarios de su aplicación. Les facilita crear una cuenta e iniciar sesión. Y lo que es aún mejor, Firebase hace que sea extremadamente fácil para los desarrolladores agregar soporte para el inicio ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-configurar-el-inicio-de-sesion-de-google-en-react-native-y-firebase/</link>
                <guid isPermaLink="false">648c75c49764040891b7e67b</guid>
                
                    <category>
                        <![CDATA[ react native ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jorge Sierra ]]>
                </dc:creator>
                <pubDate>Mon, 26 Jun 2023 18:29:28 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2023/06/React-native-Google-login.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/google-login-with-react-native-and-firebase/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Set Up Google Login in React Native &amp; Firebase</a>
      </p><p>El inicio de sesión de Google es una excelente función de inicio de sesión para ofrecer a los usuarios de su aplicación. Les facilita crear una cuenta e iniciar sesión.</p><p>Y lo que es aún mejor, Firebase hace que sea extremadamente fácil para los desarrolladores agregar soporte para el inicio de sesión de Google. Pero configurar el entorno de React Native puede crear algunos desafíos, que se cubren completamente en este tutorial.</p><p>React Native y Firebase SDK hacen que la implementación del inicio de sesión de Google sea bastante sencilla. Construyamos una aplicación simple que solo tenga un botón de inicio de sesión de Google. Una vez que el usuario inicie sesión con éxito en Google, mostraremos la información del usuario recuperada de su cuenta de Google, así como un botón de cierre de sesión.</p><p>También puede agregar Facebook Login a su aplicación si está interesado en brindar aún más opciones de inicio de sesión a sus usuarios. Puede consultar esta guía para iniciar sesión con <a href="https://instamobile.io/react-native-tutorials/facebook-login-react-native-firebase/">Facebook en React Native con Firebase</a> si desea obtener más información sobre cómo configurar el inicio de sesión con Facebook.</p><h2 id="-por-qu-usar-un-bot-n-de-inicio-de-sesi-n-de-google-en-aplicaciones-m-viles">¿Por qué usar un botón de inicio de sesión de Google en aplicaciones móviles?</h2><ol><li>El uso de Google u otros terceros puede hacer que su proceso de autenticación sea sencillo y amigable. Los usuarios no tienen que perder tiempo en el proceso de registro, lo que mejorará enormemente sus tasas de registro y retención.</li><li>Es segura y protegida.</li><li>Los usuarios confían más en Google o Facebook que en un sitio o aplicación desconocidos en Internet.</li><li>Proporciona una buena experiencia de usuario. Como usuario, tenemos poca paciencia para cualquier acción o trabajo que necesitemos hacer, especialmente en una aplicación bastante desconocida que estamos probando por primera vez.</li></ol><p>Sin más preámbulos, pasemos directamente a la parte de desarrollo de aplicaciones de este tutorial.</p><h2 id="configuraci-n-del-proyecto-firebase">Configuración del proyecto Firebase</h2><p>Vaya a <a href="https://firebase.google.com/?hl=es-419">Firebase Console</a> y cree un proyecto de Firebase:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.instamobile.io/wp-content/uploads/2020/06/create-new-firebase-project.png" class="kg-image" alt="create new firebase project" width="600" height="400" loading="lazy"><figcaption>create new firebase project</figcaption></figure><p>Aquí, necesitaremos configurar el nombre del proyecto Firebase y el identificador de la aplicación, así que primero creemos la aplicación React Native.</p><h2 id="creando-el-proyecto-react-native">Creando el proyecto React Native</h2><p>Primero, necesitamos crear un proyecto React Native usando el siguiente comando:</p><p><code>react-native init instamobile-google-login-demo</code></p><p>Aquí, hemos dado el nombre del proyecto como <strong>instamobile-google-login-demo</strong>. Ahora, necesitamos instalar el paquete <strong>react-native-google-signin</strong> usando el siguiente comando:</p><p><code>yarn add react-native-google-singin</code></p><p>El paquete <code>react-native-google-signin</code> se utiliza para implementar las funciones de autenticación de Google en la aplicación React Native. Ahora, necesitamos importar los módulos y componentes necesarios del paquete respectivo como se muestra en el fragmento de código a continuación:</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">import {
GoogleSignin,
GoogleSigninButton,
statusCodes,
} from 'react-native-google-signin';</code></pre><figcaption>import google sign-in component</figcaption></figure><p>A continuación, necesitamos crear los estados para manejar el estado de autenticación y la información del usuario. Para eso usamos el módulo <code>useState</code> como se muestra en el fragmento de código a continuación:</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">const [loggedIn, setloggedIn] = useState(false);
const [userInfo, setuserInfo] = useState([]);</code></pre><figcaption>add state</figcaption></figure><p>Ahora, necesitamos crear una función de inicio de sesión para manejar la autenticación como se muestra en el fragmento de código a continuación:</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">_signIn = async () =&gt; {
  try {
    await GoogleSignin.hasPlayServices();
    const {accessToken, idToken} = await GoogleSignin.signIn();
    setloggedIn(true);
  } catch (error) {
    if (error.code === statusCodes.SIGN_IN_CANCELLED) {
      // user cancelled the login flow
      alert('Cancel');
    } else if (error.code === statusCodes.IN_PROGRESS) {
      alert('Signin in progress');
      // operation (f.e. sign in) is in progress already
    } else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
      alert('PLAY_SERVICES_NOT_AVAILABLE');
      // play services not available or outdated
    } else {
      // some other error happened
    }
  }
};</code></pre><figcaption>add google sign-in function</figcaption></figure><p>A continuación, debemos inicializar la configuración del objeto de inicio de sesión de Google aprovechando la función <code>useEffect</code> :</p><pre><code class="language-javascript">useEffect(() =&gt; {
   GoogleSignin.configure({
     scopes: ['email'], // what API you want to access on behalf of the user, default is email and profile
     webClientId:
       '418977770929-g9ou7r9eva1u78a3anassxxxxxxx.apps.googleusercontent.com', // client ID of type WEB for your server (needed to verify user ID and offline access)
     offlineAccess: true, // if you want to access Google API on behalf of the user FROM YOUR SERVER
   });
 }, []);</code></pre><p>Por último, necesitamos una función que maneje la acción de cierre de sesión. Para eso, vamos a implementar el método <code>signOut</code> como se muestra en el fragmento de código a continuación:</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">signOut = async () =&gt; {
    try {
      await GoogleSignin.revokeAccess();
      await GoogleSignin.signOut();
      setloggedIn(false);
      setuserInfo([]);
    } catch (error) {
      console.error(error);
    }
  };</code></pre><figcaption>add Google Sign-out function</figcaption></figure><p>Ahora, también necesitamos renderizar los componentes en la pantalla. Para eso, vamos a hacer uso de varios componentes como <code>View</code> y <code>Button</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">return (
    &lt;&gt;
      &lt;StatusBar barStyle="dark-content" /&gt;
      &lt;SafeAreaView&gt;
        &lt;ScrollView
          contentInsetAdjustmentBehavior="automatic"
          style={styles.scrollView}&gt;
          &lt;Header /&gt;

          &lt;View style={styles.body}&gt;
            &lt;View style={styles.sectionContainer}&gt;
              &lt;GoogleSigninButton
                style={{width: 192, height: 48}}
                size={GoogleSigninButton.Size.Wide}
                color={GoogleSigninButton.Color.Dark}
                onPress={this._signIn}
              /&gt;
            &lt;/View&gt;
            &lt;View style={styles.buttonContainer}&gt;
              {!loggedIn &amp;&amp; &lt;Text&gt;You are currently logged out&lt;/Text&gt;}
              {loggedIn &amp;&amp; (
                &lt;Button
                  onPress={this.signOut}
                  title="LogOut"
                  color="red"&gt;&lt;/Button&gt;
              )}
            &lt;/View&gt;
          &lt;/View&gt;
        &lt;/ScrollView&gt;
      &lt;/SafeAreaView&gt;
    &lt;/&gt;
  );</code></pre><figcaption>UI code</figcaption></figure><p>Ahora bien, si ejecutamos nuestro proyecto en el emulador obtendremos los siguientes resultados:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.instamobile.io/wp-content/uploads/2020/06/google-login-first-screen.png" class="kg-image" alt="google login first screen" width="600" height="400" loading="lazy"><figcaption>Login with Google React Native</figcaption></figure><p>Bastante dulce, ¿verdad? Hemos completado la implementación (tanto de la interfaz de usuario como de la lógica empresarial) en el nivel de React Native en nuestro proyecto.</p><p>Como puede ver, tenemos un botón "Iniciar sesión con Google" que se convierte en un botón de cierre de sesión una vez que la operación de inicio de sesión se completa con éxito.</p><p>Ahora vamos a configurar el paquete de inicio de sesión de Google y la aplicación Firebase.</p><h2 id="configuraci-n-de-los-proyectos-nativos-de-ios-y-android">Configuración de los proyectos nativos de iOS y Android</h2><p>Hay algunos pasos de configuración que debemos seguir antes de que el proyecto funcione por completo. En su mayoría están relacionados con el lado nativo real de la aplicación.</p><h3 id="para-ios">Para iOS</h3><p>Aquí, en VSCode (o cualquier Terminal) simplemente ejecute <code>cd ios &amp;&amp; pod install</code>. Luego abra el archivo <em>.xcworkspace</em> en <em>Xcode</em> (desde la carpeta ios) y asegúrese de que los Pods estén incluidos:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.instamobile.io/wp-content/uploads/2020/06/install-google-login-lib-in-xcode.png" class="kg-image" alt="install google login lib in xcode" width="600" height="400" loading="lazy"><figcaption>install google login lib in xcode</figcaption></figure><h3 id="para-android"><strong>Para Android</strong></h3><p>1. Primero, necesitamos vincular el módulo nativo.</p><ul><li>En RN &gt;= 0.60 no debería necesitar hacer nada gracias a la vinculación automática.</li><li>En RN &lt; 0.60 ejecute el enlace <code>react-native link <strong><strong>react-native-google-signin</strong></strong></code>.</li></ul><p>2. Actualice <strong>android/build.gradle</strong> con la siguiente configuración:</p><pre><code class="language-java">buildscript {
    ext {
        buildToolsVersion = "27.0.3"
        minSdkVersion = 16
        compileSdkVersion = 27
        targetSdkVersion = 26
        supportLibVersion = "27.1.1"
        googlePlayServicesAuthVersion = "16.0.1" // &lt;--- use this version or newer
    }
...
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.2' // &lt;--- use this version or newer
        classpath 'com.google.gms:google-services:4.1.0' // &lt;--- use this version or newer
    }
...
allprojects {
    repositories {
        mavenLocal()
        google() // &lt;--- make sure this is included
        jcenter()
        maven {
            // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
            url "$rootDir/../node_modules/react-native/android"
        }
    }
}</code></pre><p>3. Actualice<code>android/app/build.gradle</code> con la siguiente configuración:</p><pre><code class="language-java">...
dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "com.android.support:appcompat-v7:23.0.1"
    implementation "com.facebook.react:react-native:+"
    implementation(project(":react-native-community_google-signin")) // &lt;--- add this dependency
}</code></pre><p>Verifique que vinculó el módulo nativo <code>react-native link</code> &nbsp;– pero solo si usaste <code>react-native link</code>!</p><p>En <code>android/settings.gradle</code> &nbsp;deberíamos tener las siguientes configuraciones:</p><figure class="kg-card kg-code-card"><pre><code class="language-java">...
include ':react-native-google-signin', ':app'
project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/google-signin/android')</code></pre><figcaption>setup google login for android in setting.gradle</figcaption></figure><p>A continuación, en <code>MainApplication.java</code> ,deberíamos agregar el paquete de Google como en el siguiente fragmento de código:</p><figure class="kg-card kg-code-card"><pre><code class="language-java">import co.apptailor.googlesignin.RNGoogleSigninPackage;  // &lt;--- import

public class MainApplication extends Application implements ReactApplication {

  ......

  @Override
    protected List&lt;ReactPackage&gt; getPackages() {
      return Arrays.&lt;ReactPackage&gt;asList(
          new MainReactPackage(),
          new RNGoogleSigninPackage() // &lt;-- this needs to be in the list
      );
    }
  ......

}</code></pre><figcaption>setup google login for android in MainApplication.java</figcaption></figure><h2 id="configuraci-n-para-firebase"><strong>Configuración para Firebase</strong></h2><h3 id="para-ios-1"><strong>Para iOS</strong></h3><p>Ahora, debemos comenzar con la configuración de Firebase. En Firebase, necesitamos configurar una aplicación en la nube de Google. Pero cuando configuramos el método de autenticación en Firebase, esto también crea una aplicación en la nube de Google.</p><p>Primero, necesitamos crear la aplicación Firebase iOS para obtener <strong>GoogleServiceinfo.plist</strong> como se muestra en la siguiente captura de pantalla:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.instamobile.io/wp-content/uploads/2020/06/add-new-firebase-app-name.png" class="kg-image" alt="add new firebase app name" width="600" height="400" loading="lazy"><figcaption>add new firebase app name</figcaption></figure><p>A continuación, copiamos el archivo <strong>GoogleService-info.plist</strong> al proyecto Xcode como se muestra en la siguiente captura de pantalla:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.instamobile.io/wp-content/uploads/2020/06/add-google-service-plist-to-xcode.png" class="kg-image" alt="add google service plist to xcode" width="600" height="400" loading="lazy"><figcaption>add google service plist to xcode</figcaption></figure><p>Ahora, debemos agregar la ID de cliente invertida presente en el archivo <strong>GoogleService-info.plist</strong> a los tipos de URL, como se muestra en la siguiente captura de pantalla:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.instamobile.io/wp-content/uploads/2020/06/get-reverse-client-id-from-xcode.png" class="kg-image" alt="get reverse client id from xcode" width="600" height="400" loading="lazy"><figcaption>get reverse client id from xcode</figcaption></figure><p>El siguiente paso es ir a<strong><strong> Info</strong></strong> → <strong><strong>URL Types</strong></strong> y luego completar los <strong><strong>URL Schemes</strong></strong> como se muestra en la siguiente captura de pantalla:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.instamobile.io/wp-content/uploads/2020/06/add-url-scheme-to-xcode.png" class="kg-image" alt="add url scheme to xcode" width="600" height="400" loading="lazy"><figcaption>add url scheme to xcode</figcaption></figure><h3 id="para-android-1"><strong>Para Android</strong></h3><p>Primero, necesitamos crear una aplicación de Android en Firebase. Para eso, necesitamos un nombre de paquete y un certificado <strong>SHA-1</strong> de nuestra aplicación. Luego, podemos registrar la aplicación Firebase como se muestra a continuación:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.instamobile.io/wp-content/uploads/2020/06/create-new-android-firebase-app-300x252.png" class="kg-image" alt="create new android firebase app" width="600" height="400" loading="lazy"><figcaption>create new android firebase app</figcaption></figure><p>Podemos obtener el nombre del paquete en <strong>MainApplication.java</strong> de nuestro proyecto como se destaca en el fragmento de código a continuación:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.instamobile.io/wp-content/uploads/2020/06/find-out-bundle-name-in-android-app-1024x408.png" class="kg-image" alt="find out bundle name in android app" width="600" height="400" loading="lazy"><figcaption>find out bundle name in android app</figcaption></figure><p>A continuación, podemos obtener la clave <strong>SHA-1</strong> en el archivo Keystore. En el directorio <strong>android/app</strong>, podemos ejecutar el comando:</p><figure class="kg-card kg-code-card"><pre><code class="language-shell">cd android/app ; 
keytool -exportcert -keystore debug.keystore -list -v</code></pre><figcaption>generate sha-1</figcaption></figure><p>Luego, aparecerá la clave <strong>SHA-1</strong>, como se muestra en la siguiente captura de pantalla:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.instamobile.io/wp-content/uploads/2020/06/generate-sha1-for-register-android-app-in-firebase.png" class="kg-image" alt="generate sha1 for register android app in firebase" width="600" height="400" loading="lazy"><figcaption>generate sha1 for register android app in firebase</figcaption></figure><p>Después de crear con éxito la aplicación de configuración de Firebase, debemos descargar el archivo <strong>google-services.json</strong> y copiarlo en el directorio, como se muestra en la siguiente captura de pantalla:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.instamobile.io/wp-content/uploads/2020/06/add-google-service-json-to-android-app-folder-250x300.png" class="kg-image" alt="add google service json to android app folder" width="600" height="400" loading="lazy"><figcaption>add google service json to android app folder</figcaption></figure><p>Ahora, el paso final es configurar un componente de inicio de sesión de Google en Android.</p><h3 id="instalaci-n-del-paquete-react-native-firebase">Instalación del paquete React Native Firebase</h3><p>Para instalar el paquete <strong>react-native-firebase</strong> versión 6, debemos ejecutar el siguiente comando en el símbolo del sistema de nuestro proyecto:</p><figure class="kg-card kg-code-card"><pre><code class="language-shell"># Using npm 
npm install --save @react-native-firebase/app 
# Using Yarn 
yarn add @react-native-firebase/app</code></pre><figcaption>install react native firebase core component</figcaption></figure><p>El módulo <code>@react-native-firebase/app</code> debe instalarse antes de usar cualquier otro servicio de Firebase.</p><h3 id="para-ios-2"><strong>Para iOS</strong></h3><p>Ya tenemos <strong>GoogleService-Info.plist</strong> agregado a Xcode. Lo que queda es permitir que Firebase en iOS use las credenciales. El SDK de Firebase iOS debe configurarse durante la fase de arranque de su aplicación.</p><p>Para hacer esto, debemos abrir nuestro archivo <code>/ios/{projectName}/AppDelegate.m</code> , y agregar lo siguiente:</p><p>En la parte superior del archivo, necesitamos importar el SDK de Firebase:</p><figure class="kg-card kg-code-card"><pre><code class="language-swift">#import &lt;Firebase.h&gt;</code></pre><figcaption>include Firebase</figcaption></figure><p>Dentro de su método <code>didFinishLaunchingWithOptions</code> existente, debemos agregar lo siguiente en la parte superior del método:</p><figure class="kg-card kg-code-card"><pre><code class="language-m">- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  // Add me --- \/
  if ([FIRApp defaultApp] == nil) {
    [FIRApp configure];
  }
  // Add me --- /\
  // ...
}</code></pre><figcaption>Firebase React Native</figcaption></figure><p>Finalmente, debemos ejecutar el siguiente comando para finalizar la instalación del paquete CocoaPods:</p><figure class="kg-card kg-code-card"><pre><code class="language-shell">cd ios ; pod install</code></pre><figcaption>CocoaPods install</figcaption></figure><p>Eso es todo. Ahora hemos completado la instalación del paquete principal de Firebase en iOS</p><h3 id="para-android-2"><strong>Para Android</strong></h3><p>Necesitamos configurar Firebase con credenciales de Android. Para permitir que Firebase en Android use las credenciales, el complemento de servicios de Google debe estar habilitado en el proyecto. Esto requiere la modificación de dos archivos en el directorio de Android.</p><p>Primero, agregue el complemento de servicios de Google como una dependencia dentro de su archivo <strong><strong>android/build.gradle</strong></strong>:</p><figure class="kg-card kg-code-card"><pre><code class="language-java">buildscript {
  dependencies {
    // ... other dependencies
    classpath 'com.google.gms:google-services:4.2.0'
    // Add me --- /\
  }
}
Lastly, execute the plugin by adding the following to the very bottom of your /android/app/build.gradle file:

apply plugin: 'com.google.gms.google-services'</code></pre><figcaption>add google service</figcaption></figure><h2 id="m-dulo-de-autenticaci-n-react-native-firebase">Módulo de autenticación React Native Firebase</h2><p>Una vez completada la instalación, debemos configurar el paquete principal de Firebase. A continuación, necesitamos instalar el módulo secundario para la autenticación. Para eso, necesitamos abrir una terminal y ejecutar el siguiente comando:</p><figure class="kg-card kg-code-card"><pre><code class="language-shell">yarn add @react-native-firebase/auth</code></pre><figcaption>install react native firebase auth&nbsp;</figcaption></figure><h3 id="para-ios-3"><strong>Para iOS</strong></h3><p>Solo necesitamos instalar los pods nuevamente en el símbolo del sistema:</p><figure class="kg-card kg-code-card"><pre><code>cd ios/ &amp;&amp; pod install</code></pre><figcaption>install cacao pod</figcaption></figure><h3 id="para-android-3"><strong>Para Android</strong></h3><p>Puede seguir las instrucciones en la <a href="https://rnfirebase.io/auth/usage/installation/android">documentación oficial</a> que solo se requiere si está utilizando React Native &lt;= 0.59 o necesita integrar manualmente la biblioteca.</p><h3 id="activar-el-inicio-de-sesi-n-de-google-en-firebase">Activar el inicio de sesión de Google en Firebase</h3><p>Tenemos que ir a la consola de Firebase. Luego, en la sección Autenticación, debemos hacer clic en Google como se muestra en la siguiente captura de pantalla:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.instamobile.io/wp-content/uploads/2020/06/authentication-method-in-firebase-1024x396.png" class="kg-image" alt="authentication method in firebase" width="600" height="400" loading="lazy"><figcaption>authentication method in firebase</figcaption></figure><p>A continuación, debemos habilitar la configuración con la siguiente configuración y guardar la configuración como se muestra en la captura de pantalla a continuación:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.instamobile.io/wp-content/uploads/2020/06/select-support-email-in-firebase-app.png" class="kg-image" alt="activate project support email" width="600" height="400" loading="lazy"><figcaption>activate project support email</figcaption></figure><p>En <strong>App.js</strong>, necesitamos importar la autenticación del paquete Firebase como se muestra en el fragmento de código a continuación:</p><figure class="kg-card kg-code-card"><pre><code>import auth from '@react-native-firebase/auth';</code></pre><figcaption>import firebase auth package</figcaption></figure><p>A continuación, debemos integrar la configuración de autenticación a la función de <strong><strong>sign-in</strong></strong>. Después de un inicio de sesión exitoso, almacenamos <strong><strong>accessToken</strong></strong> y <strong><strong>idToken</strong></strong> en <strong><strong>Firebase. </strong></strong>Ahora, podemos intentar iniciar sesión con Google en nuestra aplicación de demostración React Native.</p><figure class="kg-card kg-code-card"><pre><code class="language-javacript">_signIn = async () =&gt; {
    try {
      await GoogleSignin.hasPlayServices();
      const {accessToken, idToken} = await GoogleSignin.signIn();
      setloggedIn(true);
      const credential = auth.GoogleAuthProvider.credential(
        idToken,
        accessToken,
      );
      await auth().signInWithCredential(credential);
    } catch (error) {</code></pre><figcaption>Firebase Login function</figcaption></figure><p>Ahora hemos completado con éxito la integración de Google Sign-in en nuestra aplicación React Native:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.instamobile.io/wp-content/uploads/2020/06/2020-06-29-17.52.16.gif" class="kg-image" alt="result of google login with react native" width="600" height="400" loading="lazy"><figcaption>result of google login with react native</figcaption></figure><p>Podemos ver nuevos datos que se agregan a Firebase Console:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.instamobile.io/wp-content/uploads/2020/07/firebase-authentication-console.png" class="kg-image" alt="firebase authentication console" width="600" height="400" loading="lazy"><figcaption>firebase authentication console</figcaption></figure><h2 id="seguimiento-del-estado-del-usuario">Seguimiento del estado del usuario</h2><p>Para verificar el estado de inicio de sesión del usuario, usamos Firebase Auth. Para eso, necesitamos agregar el método <strong>onAuthStateChanged</strong> a <strong>useEffect</strong> para que se ejecute en cada llamada de evento <strong>componentDidMount</strong>.</p><p>Además, debemos pasar una devolución de llamada a la función denominada <strong>onAuthStateChanged</strong> como argumento, como se muestra en el fragmento de código a continuación:</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">useEffect(() =&gt; {
    .............
    const subscriber = auth().onAuthStateChanged(onAuthStateChanged);
    return subscriber; // unsubscribe on unmount
  }, []);</code></pre><figcaption>subscribe to auth state</figcaption></figure><p>En la función <strong>onAuthStateChanged</strong>, manejamos los datos del estado local como se muestra en el fragmento de código a continuación:</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">function onAuthStateChanged(user) {
    setUser(user);
    console.log(user);
    if (user) setloggedIn(true);
  }</code></pre><figcaption>set user data</figcaption></figure><p>Ahora, necesitamos almacenar los datos de usuario para el estado. Luego, intente mostrar los datos del usuario después de un inicio de sesión exitoso. Para eso, necesitamos usar la siguiente pieza de código:</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">{!user &amp;&amp; &lt;Text&gt;You are currently logged out&lt;/Text&gt;}
{user &amp;&amp; (
  &lt;View&gt;
    &lt;Text&gt;Welcome {user.displayName}&lt;/Text&gt;
    &lt;Button
      onPress={this.signOut}
      title="LogOut"
      color="red"&gt;&lt;/Button&gt;
  &lt;/View&gt;
)}</code></pre><figcaption>code for display user info</figcaption></figure><p>Obtendremos el siguiente resultado en nuestro simulador:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.instamobile.io/wp-content/uploads/2020/07/show-auth-user-name.png" class="kg-image" alt="result on show auth username" width="600" height="400" loading="lazy"><figcaption>logout firebase auth</figcaption></figure><h2 id="cerrar-sesi-n-de-firebase">Cerrar sesión de Firebase</h2><p>Para cerrar sesión, debemos eliminar todas las credenciales del usuario y revocar el token de inicio de sesión de Google.</p><p>Primero, debemos esperar a que el módulo <strong>GoogleSignin</strong> revoque el acceso y cierre la sesión. Luego, llamamos al método de <strong><strong>signOut</strong></strong> de <strong>Firebase</strong> auth para cerrar sesión con éxito.</p><p>La implementación general del código se proporciona en el siguiente fragmento de código:</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">signOut = async () =&gt; {
    try {
      await GoogleSignin.revokeAccess();
      await GoogleSignin.signOut();
      auth()
        .signOut()
        .then(() =&gt; alert('Your are signed out!'));
      setloggedIn(false);
      // setuserInfo([]);
    } catch (error) {
      console.error(error);
    }
  };</code></pre><figcaption>Firebase sign-out function</figcaption></figure><p>Como resultado, ahora podemos realizar operaciones de cierre de sesión como se muestra en el fragmento de código a continuación:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.instamobile.io/wp-content/uploads/2020/07/firebase-signout-result.gif" class="kg-image" alt="firebase react native sign out result" width="600" height="400" loading="lazy"><figcaption>firebase react native logout</figcaption></figure><h2 id="conclusion"><strong>Conclusion</strong></h2><p>En este tutorial, aprendimos a configurar Google Login, además de almacenar un token de acceso, aprovechando Firebase en nuestro proyecto React Native.</p><p>Primero, creamos el proyecto React Native con todos los componentes necesarios y configuraciones de funciones. Luego, aprendimos cómo configurar Google Sign In y Firebase para las plataformas Android e iOS. Finalmente, configuramos Firebase en la aplicación React Native usando un paquete de Firebase y mostramos los datos del usuario junto con el botón de cierre de sesión.</p><p>Puede descargar el código fuente completo de este tutorial desde <a href="https://github.com/florion101/firebase-google-login-react-native">Github</a>.</p><p>La mejor parte es que Firebase y Google Auth son compatibles con todos los lenguajes de desarrollo móvil, como <a href="https://instaflutter.com/">Flutter</a>, <a href="https://iosapptemplates.com/">Swift</a> o <a href="https://instakotlin.com/">Kotlin</a>. Los pasos de configuración y el enfoque arquitectónico son exactamente los mismos.</p><p>Si te gustó este tutorial de React Native, dame una estrella en el repositorio de <a href="https://github.com/florion101/firebase-google-login-react-native">Github</a> y compártelo con tu comunidad. Puede consultar aún más proyectos<a href="https://instamobile.io/mobile-templates/react-native-templates-free/"> React Native gratuitos</a> en Instamobile. ¡Salud!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ ¿Qué es Tailwind CSS? Guía para principiantes ]]>
                </title>
                <description>
                    <![CDATA[ Escribir CSS puede ser realmente difícil. Y punto. Y lo entiendo: puede ser frustrante plasmar tus propias ideas o los diseños que recibes de tu equipo de diseño. Estoy seguro de que muchos de ustedes han pasado por el mismo dolor al menos un par de veces en sus carreras ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/que-es-tailwind-css-guia-para-principiantes/</link>
                <guid isPermaLink="false">63cc087a700708073437c2a5</guid>
                
                    <category>
                        <![CDATA[ Tailwind ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jorge Sierra ]]>
                </dc:creator>
                <pubDate>Thu, 26 Jan 2023 18:52:43 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2023/01/Group-69.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/what-is-tailwind-css-a-beginners-guide/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">What is Tailwind CSS? A Beginner's Guide</a>
      </p><p>Escribir CSS puede ser realmente difícil. Y punto. Y lo entiendo: puede ser frustrante plasmar tus propias ideas o los diseños que recibes de tu equipo de diseño.</p><p>Estoy seguro de que muchos de ustedes han pasado por el mismo dolor al menos un par de veces en sus carreras de desarrollo.</p><p>Pues ya no. Porque es hora de aprender una interesante herramienta que nos quita gran parte de la carga. Y no, no es Bootstrap: se llama Tailwind CSS.</p><p>Aunque Tailwind existe desde hace tiempo, es posible que aún no lo conozcas. Puede que no hayas oído hablar de él o que no sepas si aprender una nueva tecnología relacionada con CSS te hará la vida más fácil.</p><p>Y, de hecho, existen muchas formas de escribir CSS, como Vanilla CSS3, LESS, SCSS, Bootstrap, styled-components, Windi CSS y muchas más... uf. Menuda lista, ¿verdad?</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/08/spongebob-long-list.gif" class="kg-image" alt="spongebob-long-list" width="600" height="400" loading="lazy"></figure><p>Espero que esta breve guía te ayude a entender Tailwind CSS y sus beneficios para que puedas decir "Este es. Esto es".</p><p>Bueno, basta de cháchara. Entremos de lleno.</p><h2 id="-qu-es-el-css-at-mico">¿Qué es el CSS atómico?</h2><p>Antes de adentrarnos en Tailwind CSS, vamos a entender qué es Atomic CSS. Según <a href="https://css-tricks.com/lets-define-exactly-atomic-css/">CSS Tricks</a></p><blockquote><em><em>"</em></em>CSS atómico es el enfoque de la arquitectura CSS que favorece las clases pequeñas y de propósito único con nombres basados en la función visual.<em><em>"</em></em></blockquote><p>Es como hacer clases que se supone que tienen un único propósito. Por ejemplo, hagamos una clase <code>bg-blue</code> con el siguiente CSS:</p><pre><code class="language-css">.bg-blue {
  background-color: rgb(81, 191, 255);
}

</code></pre><p>Ahora bien, si añadimos esta clase a una etiqueta <code>&lt;h1&gt;</code>, obtendrá un fondo azul cuyo color será <code>rgb(81, 191, 255)</code> como se puede ver en el código anterior.</p><p>Y aquí está el HTML:</p><pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
  &lt;head&gt;
    &lt;meta charset="UTF-8" /&gt;
    &lt;meta http-equiv="X-UA-Compatible" content="IE=edge" /&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&gt;
    &lt;title&gt;Document&lt;/title&gt;
    &lt;link rel="stylesheet" href="style.css" /&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div&gt;&lt;h1 class="bg-blue"&gt;Hello world!&lt;/h1&gt;&lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><p>Así que este HTML resultará en algo como esto:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/08/img2-1.PNG" class="kg-image" alt="img2-1" width="600" height="400" loading="lazy"></figure><p>Ahora imagínese escribir algo tan útil <strong><strong>single-purpose CSS rules</strong></strong> y guardarlos todos en un <strong>archivo <strong>global CSS</strong></strong>. Sé que es una inversión de una sola vez, pero piensa en esto: ahora puedes utilizar estas clases de ayuda de un solo propósito desde donde quieras.</p><p>Sólo necesitas que tu archivo HTML consuma ese archivo CSS global, y ya está. Ahora también puedes utilizar combinaciones de estas clases de ayuda en una sola etiqueta HTML.</p><p>Veamos otro ejemplo, ¿de acuerdo?</p><p>Vamos a crear un archivo CSS con las siguientes reglas:</p><pre><code class="language-css">.bg-blue {
  background-color: rgb(81, 191, 255);
}
.bg-green {
  background-color: rgb(81, 255, 90);
}
.text-underline {
  text-decoration: underline;
}
.text-center {
  text-align: center;
}
.font-weight-400 {
  font-weight: 400;
}
</code></pre><p>y luego consumirlo en nuestro archivo HTML de la siguiente manera:</p><pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
  &lt;head&gt;
    &lt;meta charset="UTF-8" /&gt;
    &lt;meta http-equiv="X-UA-Compatible" content="IE=edge" /&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&gt;
    &lt;title&gt;Document&lt;/title&gt;
    &lt;link rel="stylesheet" href="style.css" /&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div&gt;&lt;h1 class="bg-blue"&gt;Hello world 1&lt;/h1&gt;&lt;/div&gt;
    &lt;div&gt;&lt;h1 class="text-underline"&gt;Hello world 2&lt;/h1&gt;&lt;/div&gt;
    &lt;div class="text-center"&gt;
      &lt;h1 class="bg-green font-weight-400 text-underline"&gt;Hello world 3&lt;/h1&gt;
    &lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><p>Bien, ahora esto generará el siguiente resultado:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/08/img3-1.PNG" class="kg-image" alt="img3-1" width="600" height="400" loading="lazy"></figure><h3 id="-puntos-a-tener-en-cuenta-aqu-"><strong>?️ </strong>Puntos a tener en cuenta aquí:</h3><ul><li><strong>Combinación de varias clases de ayuda<strong>:</strong></strong>Mira cómo he combinado varias clases de ayuda en la línea 14 de la etiqueta <code>&lt;h1&gt;</code>, concretamente <code>bg-green</code>, <code>font-weight-400</code> y <code>text-underline</code>. Todo surtió efecto en mi texto <strong><strong>Hello world 3</strong></strong>.</li><li><strong>Reutilización de clases auxiliares<strong>:</strong></strong> En el ejemplo anterior, fíjese en cómo <code>text-underline</code> se utiliza varias veces en las líneas 12 y 14.</li></ul><p>Mira cómo hemos podido añadir diferentes estilos sin ni siquiera salir de la página HTML. Bueno, puede que digas: "Oye, tuvimos que escribir esas clases de ayuda o utilidad en el archivo CSS global... ¿Qué pasa con eso?". Bueno, lo entiendo. Esa fue definitivamente la inversión inicial que tuvimos que hacer para empezar.</p><p>Y, por supuesto, quién sabe cuántas de estas clases de ayuda o utilidad de propósito único tendríamos que hacer si quisiéramos seguir esta arquitectura <em>Atomic </em>CSS.</p><p>Y ahí es donde entra Tailwind CSS. El concepto de CSS atómico no es nuevo, pero Tailwind CSS lo lleva a otro nivel.</p><h2 id="tailwind-css-un-framework-css-de-utilidades-b-sicas">Tailwind CSS - Un framework CSS de utilidades básicas</h2><p>Tailwind CSS, según su propio sitio web, es un "framework CSS que prioriza las utilidades" que proporciona varias de estas clases de utilidades de un solo propósito que puedes utilizar directamente dentro de tu marcado para diseñar un elemento.</p><p>Algunas de las clases de utilidad que utilizo con frecuencia estos días son:</p><ul><li><strong><strong>flex</strong></strong>: Se utiliza para aplicar Flexbox a un <code>&lt;div&gt;</code></li><li><strong><strong>items-center</strong></strong>: para aplicar la propiedad CSS <code>align-items: center;</code> a un <code>&lt;div&gt;</code></li><li><strong><strong>rounded-full</strong></strong>: para hacer circular una imagen, etc.</li></ul><p>En serio, no me es posible enumerarlas todas porque hay muchas de estas clases de utilidad. Pero la mejor parte es, que no tenemos que escribir estas clases de utilidad nosotros mismos y mantenerlos en cualquier archivo CSS global. Las obtenemos directamente de Tailwind.</p><p>Puedes obtener una lista de todas las clases de utilidad que ofrece Tailwind en la página de <a href="https://tailwindcss.com/docs/installation">documentación.</a> También si estás trabajando en VS Code, puedes instalar una extensión llamada <a href="https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss">Tailwind CSS IntelliSense</a> y te dará auto-sugerencias mientras sigues escribiendo las clases de utilidad, como se muestra en la imagen de abajo.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/08/img4-1.PNG" class="kg-image" alt="img4-1" width="600" height="400" loading="lazy"></figure><h3 id="c-mo-configurar-tailwind-css">Cómo configurar Tailwind CSS</h3><p>Hay varias formas de configurar el CSS de Tailwind en tu proyecto, todas ellas mencionadas en su <a href="https://tailwindcss.com/docs/installation">documentación</a>.</p><p>Tailwind CSS funciona sin problemas con una plétora de frameworks como Next, React, Angular y más, e incluso con nuestro HTML original.</p><p>Para la siguiente demostración práctica estoy utilizando <strong>Tailwind CSS con una aplicación Next</strong>. Para configurar una aplicación Next con Tailwind CSS directamente, utilice el siguiente comando:</p><p>Con <code>npx</code></p><pre><code class="language-shell">npx create-next-app --example with-tailwindcss with-tailwindcss-app
</code></pre><p>O con <code>yarn</code></p><pre><code class="language-shell">yarn create next-app --example with-tailwindcss with-tailwindcss-app
</code></pre><p>Una vez configurado el proyecto, puede sumergirse en el siguiente paso para crear un componente de tarjeta básico</p><h3 id="demostraci-n-pr-ctica">Demostración práctica</h3><p>Construyamos un componente de tarjeta en un proyecto Next.</p><pre><code class="language-jsx">// Card.js file
// to be rendered in index.js

import React from "react";

const Card = () =&gt; {
  return (
    &lt;div className="relative w-96 m-3 cursor-pointer border-2 shadow-lg rounded-xl items-center"&gt;
      {/* Image */}
      &lt;div className="flex h-28 bg-blue-700 rounded-xl items-center justify-center"&gt;
        &lt;h1 className="absolute mx-auto text-center right text-2xl text-white"&gt;
          Image goes here
        &lt;/h1&gt;
      &lt;/div&gt;

      {/* Description */}
      &lt;div className="p-2 border-b-2"&gt;
        &lt;h6&gt;
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Facilis
          beatae nulla, atque et sunt ad voluptatum quidem impedit numquam quia?
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Facilis
          beatae nulla, atque et sunt ad voluptatum quidem impedit numquam quia?
        &lt;/h6&gt;
      &lt;/div&gt;

      {/* Tech stack used */}
      &lt;div className="flex flex-wrap items-center m-2"&gt;
        &lt;span className=" border border-blue-300 rounded-2xl px-2 my-1 mx-1"&gt;
          #React
        &lt;/span&gt;
        &lt;span className=" border border-blue-300 rounded-2xl px-2 my-1 mx-1"&gt;
          #Redux
        &lt;/span&gt;
        &lt;span className=" border border-blue-300 rounded-2xl px-2 my-1 mx-1"&gt;
          #Javascript
        &lt;/span&gt;
      &lt;/div&gt;

      {/* Links */}
      &lt;div className="flex flex-wrap items-center rounded-b-xl border-t-2 bg-white"&gt;
        &lt;button className="border rounded-2xl bg-blue-600 text-white shadow-sm p-1 px-2 m-2"&gt;
          Go to Project
        &lt;/button&gt;
        &lt;button className="border-2 border-blue-600 rounded-2xl text-blue-600 shadow-sm p-1 px-2 m-2"&gt;
          Github
        &lt;/button&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default Card;
</code></pre><p>El resultado es la siguiente tarjeta que se muestra en la interfaz de usuario:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/08/img5-1.PNG" class="kg-image" alt="img5-1" width="600" height="400" loading="lazy"></figure><p>Mira lo fácil que es dar estilo al componente de la tarjeta sin siquiera salir del archivo Card.js. No es necesario escribir ningún archivo CSS adicional.</p><p>Utilizando <code>flex</code> con un <code>&lt;div&gt;</code> aplica regla CSS <code>display: flex;</code>. ¿Desea añadir <code>position: relative;</code> a un <code>&lt;div&gt;</code>? Sólo tiene que añadir <code>relative</code> en la <code>className</code> y ya está.</p><p>También podemos añadir diferentes modificadores como <code>hover</code>, <code>active</code>, <code>focus</code> y así sucesivamente para renderizar condicionalmente las clases de utilidad. Es posible aplicar reglas CSS complejas como esta:</p><pre><code class="language-css">.some-class-name {
          --tw-space-x-reverse: 0;
          margin-right: calc(0.5rem * var(--tw-space-x-reverse));
          margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
}
</code></pre><p>Con solo mencionar <code>space-x-2</code> en la etiqueta <code>&lt;div&gt;</code>. Genial, ¿verdad?</p><p>¿Tenemos que mencionar explícitamente estos estilos en algún archivo CSS global? Por supuesto que no. Tailwind lo hace automáticamente por nosotros. Esa es la belleza de Tailwind.</p><p>Aún no hemos terminado... hay muchas otras ventajas. Veámoslas ahora.</p><h3 id="ventajas-de-tailwind-css">Ventajas de Tailwind CSS</h3><h4 id="just-in-time-jit-ofrece-tiempos-de-compilaci-n-rapid-simos"><strong>Just-In-Time (JIT) </strong>ofrece tiempos de compilación rapidísimos</h4><p>Antes de la versión 3 de Tailwind, se purgaban todos los estilos para eliminar los que no se utilizaban, de modo que la compilación de producción fuera lo más pequeña posible.</p><p>Según Tailwind, la compilación de producción solía tener entre 5 y 10 kB. Pero esa es la historia en producción. En un entorno de desarrollo, el CSS puede llegar a ser realmente grande, especialmente si usamos mucha configuración personalizada.</p><p>Con la v3 y superiores, Tailwind lanzó una nueva característica llamada <strong>compilador Just-in-Time</strong>. El compilador JIT evita compilar todo el CSS por adelantado y compila sólo el CSS como y cuando lo necesitamos.</p><p>El resultado son tiempos de creación rapidísimos en todos los entornos. Y como los estilos se generan a medida que los necesitamos, no es necesario purgar los estilos no utilizados. Esto significa que el CSS en todos los entornos será el mismo. Esto nos ayuda a deshacernos del miedo a que cualquier CSS importante sea purgado en producción.</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.17833333333333%;" class="fluid-width-video-wrapper">
            <iframe src="https://www.youtube.com/embed/3O_3X7InOw8?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" title="Embedded content" loading="lazy" name="fitvid0" data-dashlane-frameid="455" 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-size: 22px; vertical-align: middle; position: absolute; top: 0px; left: 0px; width: 720px; height: 404.484px;"></iframe>
          </div>
        </div>
      </figure><h4 id="es-obstinado-y-flexible-al-mismo-tiempo">Es obstinado y flexible al mismo tiempo</h4><p>El CSS de Tailwind es obstinado. Especifica algunas restricciones cuando se trata de estilo, y si me preguntas esto es bueno, ya que nos ayuda a mantener la parte de diseño a los que realmente lo entienden.</p><p>Basta con mirar una de las clases de utilidad para añadir un <code>box-shadow</code> a su <code>&lt;div&gt;</code>.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/08/img6-1.PNG" class="kg-image" alt="img6-1" width="600" height="400" loading="lazy"></figure><p>Como puede ver, solo hay 8 variantes de sombra que Tailwind proporciona. Hay valores preestablecidos para el desplazamiento vertical y horizontal, el desenfoque, la dispersión, el color y la opacidad. Por eso Tailwind es obstinado.</p><p>Trata de dar una opinión sobre qué valores de propiedad elegir en casi todas las propiedades de estilo que existen. Y créeme, para la mayoría de los casos, estas 8 variantes (para <code>box-shadow</code>) será más que suficiente para crear una gran interfaz de usuario.</p><p>Por ejemplo, en el ejemplo práctico anterior, he utilizado <code>shadow-lg</code> en la clase principal <code>&lt;div&gt;</code> para conseguir esa bonita sombra exterior.</p><p>El uso de la misma variante de una determinada clase de utilidad en distintas zonas de la interfaz de usuario también garantiza la uniformidad en toda la aplicación y mejora la experiencia del usuario.</p><p>Pero en caso de que necesites algún valor realmente personalizado para algún estilo en particular, puedes conseguirlo añadiendo un tema personalizado en la función <code>tailwind.config.js</code>. Por ejemplo, para obtener un <code>shadow-3xl</code> (Tailwind no proporciona <code>shadow-3xl</code> fuera de la caja) puede añadir las siguientes líneas en el archivo <code>module.exports</code> en <code>tailwind.config.js</code>:</p><pre><code class="language-js">module.exports = {
  theme: {
    extend: {
      boxShadow: {
        '3xl': '0 35px 60px -15px rgba(0, 0, 0, 0.3)',
      }
    }
  }
}
</code></pre><p>Y ahora, con la llegada del JIT, también puede utilizar un valor arbitrario entre corchetes <code>[]</code> como las siguientes:</p><pre><code class="language-jsx">&lt;div class="shadow-[0_35px_60px_-15px_rgba(0,0,0,0.3)]"&gt;
  // Rest of your code goes here
&lt;/div&gt;
</code></pre><p>El uso de valores arbitrarios puede ser útil cuando se necesita un estilo específico en solo unos pocos lugares. Y en este caso, crear un tema para ello en el archivo <code>tailwind.config.js</code> puede parecer innecesario.</p><h2 id="mi-opini-n">Mi opinión</h2><p>Realmente espero haber podido hacerte entender qué es Tailwind CSS y qué puedes hacer con él.</p><p>Tailwind es un framework CSS que nos proporciona clases de utilidad de un solo propósito que son opinables en su mayor parte, y que nos ayudan a diseñar nuestras páginas web desde dentro de nuestro marcado o archivos .js/.jsx/.ts/.tsx.</p><p>En mi opinión, Tailwind es sencillo y fácil de entender. Es cierto que puede llevar algún tiempo entender todos los nombres de las clases de utilidad, pero no te preocupes: puedes consultar su documentación siempre que te atasques.</p><p>Y para todos los principiantes que están empezando su andadura en el desarrollo web, es muy importante saber qué es CSS3 antes de explorar Tailwind (o cualquier otro framework CSS como Bootstrap, Windi CSS, etc.).</p><h2 id="conclusi-n">Conclusión</h2><p>Gracias por leer. Espero que hayas disfrutado leyendo sobre Tailwind CSS en este artículo y que lo hayas encontrado útil.</p><p>Considera la posibilidad de compartirlo con tus amigos, te lo agradecería mucho. Sígueme en LinkedIn y Twitter (ver más abajo) y permanece atento a más contenidos increíbles. ¡Hasta la vista! ?</p><h2 id="enlace-social">Enlace Social</h2><p><a href="https://twitter.com/_sohamderoy">Twitter</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ ¿Qué es un Helm Chart? Un tutorial para principiantes en Kubernetes ]]>
                </title>
                <description>
                    <![CDATA[ Kubernetes [https://azure.microsoft.com/es-es/products/kubernetes-service/?WT.mc_id=containers-19838-ludossan] es una herramienta muy útil para los desarrolladores nativos de la nube. Pero no cubre todas las bases por sí sola: hay algunas cosas que Kubernetes no puede resolver o que están fuera de su alcance. Esta es una de las razones por las que los proyectos de ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/que-es-un-helm-chart-un-tutorial-para-principiantes-en-kubernetes/</link>
                <guid isPermaLink="false">63cbf391700708073437c1e0</guid>
                
                    <category>
                        <![CDATA[ helm ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jorge Sierra ]]>
                </dc:creator>
                <pubDate>Thu, 26 Jan 2023 18:28:32 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2023/01/604640a8a7946308b768453d.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/what-is-a-helm-chart-tutorial-for-kubernetes-beginners/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">What is a Helm Chart? A Tutorial for Kubernetes Beginners</a>
      </p><p><a href="https://azure.microsoft.com/es-es/products/kubernetes-service/?WT.mc_id=containers-19838-ludossan">Kubernetes </a>es una herramienta muy útil para los desarrolladores nativos de la nube. Pero no cubre todas las bases por sí sola: hay algunas cosas que Kubernetes no puede resolver o que están fuera de su alcance.</p><p>Esta es una de las razones por las que los proyectos de código abierto son tan fantásticos. Ayudan a que herramientas increíbles sean aún más increíbles cuando las combinamos con otras herramientas increíbles de código abierto. Y a menudo estas herramientas se desarrollaron con el único propósito de llenar los vacíos. Una de estas herramientas es Helm.</p><h2 id="-qu-es-helm">¿Qué es Helm?</h2><p><a href="https://helm.sh/">Helm </a>es ampliamente conocido como "el gestor de paquetes para <a href="https://azure.microsoft.com/es-es/products/kubernetes-service/?WT.mc_id=containers-19838-ludossan">Kubernetes</a>". Aunque se presenta así, su alcance va mucho más allá del de un simple gestor de paquetes. Sin embargo, empecemos por el principio.</p><p>Helm es un proyecto de código abierto que fue creado originalmente por <a href="https://deislabs.io/">DeisLabs </a>y donado a <a href="https://azure.microsoft.com/es-es/blog/announcing-cncf/?WT.mc_id=containers-19838-ludossan">CNCF</a>, que ahora lo mantiene. El objetivo original de Helm era proporcionar a los usuarios una forma mejor de gestionar todos los archivos YAML de <a href="https://azure.microsoft.com/es-es/products/kubernetes-service/?WT.mc_id=containers-19838-ludossan">Kubernetes </a>que creamos en los proyectos de <a href="https://azure.microsoft.com/es-es/products/kubernetes-service/?WT.mc_id=containers-19838-ludossan">Kubernetes</a>.</p><p>El camino que <a href="https://learn.microsoft.com/es-es/azure/aks/kubernetes-helm?WT.mc_id=containers-19838-ludossan">Helm </a>tomó para resolver este problema fue crear Helm <a href="https://learn.microsoft.com/es-es/azure/aks/kubernetes-helm?WT.mc_id=containers-19838-ludossan">Charts</a>. Cada gráfico es un paquete con uno o más manifiestos de <a href="https://azure.microsoft.com/es-es/products/kubernetes-service/?WT.mc_id=containers-19838-ludossan">Kubernetes</a>: un gráfico puede tener gráficos hijos y gráficos dependientes.</p><p>Esto significa que Helm instala todo el árbol de dependencias de un proyecto si ejecuta el comando install para el gráfico de nivel superior. Sólo tiene que ejecutar un único comando para instalar toda su aplicación, en lugar de listar los ficheros a instalar mediante <code>kubectl</code>.</p><p>Charts también te permite versionar tus archivos de manifiesto, al igual que hacemos con Node.js o cualquier otro paquete. Esto te permite instalar versiones específicas de gráficos, lo que significa mantener configuraciones específicas para tu infraestructura en forma de código.</p><p>Helm también mantiene un historial de versiones de todas las cartas desplegadas, por lo que puede volver a una versión anterior si algo va mal.</p><p><a href="https://learn.microsoft.com/es-es/azure/aks/kubernetes-helm?WT.mc_id=containers-19838-ludossan">Helm </a>soporta <a href="https://azure.microsoft.com/es-es/products/kubernetes-service/?WT.mc_id=containers-19838-ludossan">Kubernetes </a>de forma nativa, lo que significa que no tienes que escribir ningún archivo de sintaxis complejo ni nada para empezar a usar Helm. Solo tienes que soltar tus archivos de plantilla en un nuevo gráfico y listo.</p><p>Pero, ¿por qué deberíamos utilizarlo? La gestión de los manifiestos de aplicación puede hacerse fácilmente con unas pocas combinaciones de comandos.</p><h2 id="-por-qu-utilizar-helm">¿Por qué utilizar Helm?</h2><p>Helm realmente brilla donde Kubernetes no llegó. Por ejemplo, las plantillas. El alcance del proyecto Kubernetes es tratar con sus contenedores para usted, no sus archivos de plantilla.</p><p>Esto dificulta en exceso la creación de archivos realmente genéricos que puedan utilizarse en un equipo o una organización grandes, con muchos parámetros diferentes que deben configurarse para cada archivo.</p><p>Y también, ¿cómo versionar información sensible usando Git cuando los archivos de plantilla son texto plano?</p><p>La respuesta: Ir a las plantillas. Helm le permite añadir variables y utilizar funciones dentro de sus archivos de plantilla. Esto lo hace perfecto para aplicaciones escalables que eventualmente necesitarán cambiar sus parámetros. Veamos un ejemplo.</p><p>Tengo un proyecto de código abierto llamado <a href="https://github.com/khaosdoctor/zaqar/">Zaqar</a>, un microservicio de correo electrónico simple para Node.js que se comunica con SendGrid. El proyecto se compone básicamente de un servicio, un despliegue y un autoescalador.</p><p>Tomemos como ejemplo el archivo de despliegue. Tendría algo como esto</p><pre><code class="language-yaml">apiVersion: apps/v1
kind: Deployment
metadata:
  name: zaqar
  namespace: default
  labels:
    app: zaqar
    version: v1.0.0
    env: production
spec:
  replicas: 1
  selector:
    matchLabels:
      app: zaqar
      env: production
  template:
    metadata:
      labels:
        app: zaqar
        version: v1.0.0
        env: production
    spec:
      containers:
        - name: zaqar
          image: "khaosdoctor/zaqar:v1.0.0"
          imagePullPolicy: IfNotPresent
          env:
            - name: SENDGRID_APIKEY
              value: "MY_SECRET_KEY"
            - name: DEFAULT_FROM_ADDRESS
              value: "my@email.com"
            - name: DEFAULT_FROM_NAME
              value: "Lucas Santos"
          ports:
            - name: http
              containerPort: 3000
              protocol: TCP
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 250m
              memory: 256Mi</code></pre><p>Si quisiera usar esta plantilla en un CI pipeline, o publicarla en mi GitHub, necesitaría reemplazar las partes variables por marcadores de posición. Así que podemos reemplazar estos textos con la información requerida.</p><p>En este caso, tanto la etiqueta de versión como la etiqueta <code>env</code> y las variables de entorno se sustituirían por marcadores de posición, así:</p><pre><code class="language-yaml">apiVersion: apps/v1
kind: Deployment
metadata:
  name: zaqar
  namespace: default
  labels:
    app: zaqar
    version: #!VERSION!#
    env: #!ENV!#
spec:
  replicas: 1
  selector:
    matchLabels:
      app: zaqar
      env: #!ENV!#
  template:
    metadata:
      labels:
        app: zaqar
        version: #!VERSION!#
        env: #!ENV!#
    spec:
      containers:
        - name: zaqar
          image: "khaosdoctor/zaqar:#!VERSION!#"
          imagePullPolicy: IfNotPresent
          env:
            - name: SENDGRID_APIKEY
              value: "#!SENDGRID_KEY!#"
            - name: DEFAULT_FROM_ADDRESS
              value: "#!FROM_ADDR!#"
            - name: DEFAULT_FROM_NAME
              value: "#!FROM_NAME!#"
          ports:
            - name: http
              containerPort: 3000
              protocol: TCP
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 250m
              memory: 256Mi</code></pre><p>Ahora podemos ejecutar nuestro CI pipeline. Pero antes de hacerlo, tenemos que reemplazar nuestros marcadores de posición con los valores reales.</p><p>Para ello podemos utilizar <code>sed</code> y su "super fácil" <code>sed 's/#!PLACEHOLDER!#/replacement/g'</code>, y canalizar esto hacia abajo hasta que terminemos todos los marcadores de posición. El comando final sería algo como esto:</p><pre><code class="language-bash">cat deploy.yaml | \
    sed 's/#!ENV!#/production/g' | \
    sed 's/#!VERSION!#/v1.0.0/g' | \
    sed 's/#!SENDGRID_KEY!#/MyKey/g' | \
    sed 's/#!FROM_ADDR!#/my@email.com/g' | \
    sed 's/#!FROM_NAME!#/Lucas Santos/g'</code></pre><p>Por defecto, sed envía todo al archivo<code>stdout</code>, para que podamos añadir otra tubería a <code>kubectl -f</code>, como <code>&lt;all the command from before&gt; | kubectl -f -</code>. Entonces tendremos nuestro despliegue en su lugar. El único problema es que tenemos que hacer lo mismo para todos los demás archivos.</p><p>Ahora imagina un proyecto más grande, con muchas otras variables y marcadores de posición. Probablemente, escribirías un script para hacerlo por ti, ¿verdad? Ese script es Helm.</p><p>Cuando se crea un Gráfico (más sobre esto más adelante), tenemos un árbol de directorios específico que debemos seguir para que Helm entienda lo que queremos hacer. Dentro del directorio <code>templates</code> podemos añadir nuestros archivos de manifiesto, <strong>con</strong> <strong>plantillas go nativas</strong>, de la siguiente manera:</p><pre><code class="language-yaml">apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.name }}
  namespace: {{ default .Release.Namespace .Values.namespace }}
  labels:
    app: {{ .Values.name }}
    version: {{ .Values.image.tag }}
    env: {{ .Values.env }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ .Values.name }}
      env: {{ .Values.env }}
  template:
    metadata:
      labels:
        app: {{ .Values.name }}
        version: {{ .Values.image.tag }}
        env: {{ .Values.env }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "khaosdoctor/zaqar:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          env:
            - name: SENDGRID_APIKEY
              value: {{ required "You must set a valid Sendgrid API key" .Values.environment.SENDGRID_APIKEY | quote }}
            - name: DEFAULT_FROM_ADDRESS
              value: {{ required "You must set a default from address" .Values.environment.DEFAULT_FROM_ADDRESS | quote }}
            - name: DEFAULT_FROM_NAME
              value: {{ required "You must set a default from name" .Values.environment.DEFAULT_FROM_NAME | quote }}
          ports:
            - name: http
              containerPort: 3000
              protocol: TCP
          resources:
            {{- toYaml .Values.resources | nindent 12 }}</code></pre><p>Todos esos valores pueden obtenerse a partir de un archivo <code>Values.yaml</code> (para los valores por defecto), o puede configurarlos en la CLI utilizando el comando con su bandera <code>--set &lt;path&gt; value</code>.</p><p>Si queremos instalar nuestro gráfico podemos emitir el siguiente comando:</p><pre><code class="language-bash">helm upgrade --install --create-namespace myChart ./path/to/my/chart \
  --set image.tag=v1.0.0 \
  --set env=production \
  --set environment.SENDGRID_APIKEY=myKey \
  --set environment.DEFAULT_FROM_ADDRESS="my@email.com" \
  --set environment.DEFAULT_FROM_NAME="Lucas Santos"</code></pre><p>Helm también nos permite utilizar funciones dentro de nuestros despliegues. Así que podemos tener <code>default</code> para volver a los valores por defecto si no se rellenan, como el espacio de nombres. O podemos tener <code>required</code> que muestra un mensaje y no instala el gráfico si no se proporciona el valor, que es el caso de nuestras variables de entorno.</p><p>Hay muchas otras funciones útiles en sus documentos.</p><p>Ahora podemos no sólo gestionar de forma más eficiente los recursos de nuestra aplicación, sino también publicar estos recursos en un sistema de versiones de código abierto sin ningún tipo de molestia ni problema de seguridad.</p><h2 id="c-mo-crear-un-cuadro-de-mando">Cómo crear un cuadro de mando</h2><p>Es bastante fácil crear un gráfico en Helm. Primero, necesita tener <a href="https://helm.sh/es/docs/intro/quickstart/">Helm instalado</a>. Luego, simplemente escriba <code>helm create &lt;chart name&gt;</code> y creará un directorio lleno de archivos y otros directorios. Esos archivos son necesarios para que Helm cree un gráfico.</p><p>Veamos con más detalle qué aspecto tiene este árbol de archivos y qué archivos contiene:</p><ul><li><strong><strong>chart.yaml:</strong></strong> Aquí es donde pondrás la información relacionada con tu gráfico. Eso incluye la versión del gráfico, el nombre y la descripción para que puedas encontrarlo si lo publicas en un repositorio abierto. También en este archivo podrás establecer <a href="https://helm.sh/es/docs/topics/provenance/">dependencias</a> externas usando el comando <code>dependencies</code> llave.</li><li><strong><strong>values.yaml</strong></strong>: Como vimos antes, este es el archivo que contiene los valores por defecto para las variables.</li><li><strong><strong>templates (dir):</strong></strong> Este es el lugar donde pondrás todos tus archivos de manifiesto. Todo lo que haya aquí se transmitirá y creará en Kubernetes.</li><li><strong><strong>charts:</strong></strong>Si su gráfico depende de otro gráfico de su propiedad, o si no quiere depender de la biblioteca por defecto de Helm (el registro por defecto de donde Helm extrae los gráficos), puede traer esta misma estructura dentro de este directorio. Las dependencias de los gráficos se instalan de abajo a arriba, lo que significa que si el gráfico A depende del gráfico B, y B depende de C, el orden de instalación será C -&gt;B -&gt;A.</li></ul><p>Hay otros campos, pero estos son los más comunes, y son los obligatorios. Puedes echar un vistazo rápido al <a href="https://github.com/khaosdoctor/zaqar/tree/master/helm">repositorio de Zaqar </a>para comprobar cómo podemos publicar gráficos de código abierto.</p><p>Una nota rápida: Cuando instale Helm, asegúrese de que está instalando la versión 3. La versión 2 todavía funciona. La versión 2 todavía funciona, pero necesita un componente del lado del servidor llamado Tiller, que ata su instalación helm a un solo clúster. Helm 3 eliminó esta necesidad con la adición de varios CRDs, pero no es compatible con todas las versiones de Kubernetes.</p><h2 id="c-mo-organizar-un-cuadro-de-mando">Cómo organizar un cuadro de mando</h2><p>Vale, has creado tu gráfico, ¿y ahora qué? ¿Tenemos que descargar todo el repositorio para instalar esos gráficos? Helm tiene una <a href="https://artifacthub.io/">biblioteca pública para los gráficos más utilizados</a>, que funciona como Docker Hub.</p><p>También puede crear su propio repositorio de gráficos y <a href="https://helm.sh/docs/topics/chart_repository/">alojarlo en línea</a>. Helm bebe de la misma fuente que HomeBrew, o Linux. Puede acceder a estos repositorios para descargar los gráficos que contienen.</p><p>Dado que un repositorio de gráficos es básicamente un <code>index.yaml</code> servido desde un servidor web estático, prácticamente se puede crear un repositorio de gráficos desde cualquier lugar.</p><p>Por ejemplo, <a href="https://github.com/khaosdoctor/zaqar/tree/master/helm">Zaqar</a> está alojado en GitHub Pages y es accesible a través de mi <a href="https://beacons.ai/lsantos">dominio</a>. Cuando Helm busca un <code>index.yaml</code> en realidad, está buscando la lista de versiones disponibles de ese gráfico, sus compendios SHA256 y la ubicación del archivo empaquetado <code>.tgz</code> para descargar el gráfico. Esto es más o menos lo que NPM hace bajo el capó (demasiado simplificado).</p><p>Esto significa que no necesitas tener tu repositorio clonado para siempre, y tus gráficos también pueden ser privados. Sólo necesitas crear un repositorio de gráficos.</p><p>Incluso puedes utilizar <a href="https://learn.microsoft.com/en-us/azure/container-registry/container-registry-helm-repos?WT.mc_id=containers-19838-ludossan">servicios alojados como Azure CR</a> para hacer el trabajo, o puedes tener una solución completa llamada <a href="https://github.com/helm/chartmuseum">Chart Museum</a>, que te permite almacenar tus gráficos y te proporciona una interfaz de usuario ordenada.</p><h2 id="conclusi-n">Conclusión</h2><p>Helm está aquí para quedarse. Ha ayudado y ayudará a muchos desarrolladores de Kubernetes durante mucho tiempo.</p><p>Si quieres saber cómo usar Helm, puedes consultar su <a href="https://helm.sh/es/">documentación</a>, o puedes seguir este <a href="https://learn.microsoft.com/es-es/training/modules/aks-app-package-management-using-helm/?WT.mc_id=containers-19838-ludossan">módulo de aprendizaje gratuito</a> sobre cómo desplegar tus aplicaciones en Kubernetes de forma sencilla con Helm.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo configurar Go para Windows: guía rápida y sencilla ]]>
                </title>
                <description>
                    <![CDATA[ Otro gran lenguaje para añadir a tu cinturón de herramientas de desarrollador full-stack es el sencillo y productivo lenguaje de programación de propósito general Go. Gracias a un proyecto iniciado en 2007, Go vio la luz gracias al esfuerzo de algunos programadores de Google. Pusieron mucho cuidado en el diseño ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-configurar-go-para-windows-guia-rapida-y-sencilla/</link>
                <guid isPermaLink="false">63cbee9c700708073437c196</guid>
                
                    <category>
                        <![CDATA[ Programación ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jorge Sierra ]]>
                </dc:creator>
                <pubDate>Tue, 24 Jan 2023 19:53:21 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2023/01/5f9c9a97740569d1a4ca2681.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/setting-up-go-programming-language-on-windows-f02c8c14e2f/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to set up Go for Windows — a quick and easy guide</a>
      </p><p>Otro gran lenguaje para añadir a tu cinturón de herramientas de desarrollador full-stack es el sencillo y productivo lenguaje de programación de propósito general Go.</p><p>Gracias a un proyecto iniciado en 2007, Go vio la luz gracias al esfuerzo de algunos programadores de Google. Pusieron mucho cuidado en el diseño de Go para que fuera claro y coherente en sus características de lenguaje y bibliotecas estándar, haciendo que Go fuera fácil y divertido de usar.</p><p>Es lo mejor del código abierto… pero no olvide que distingue entre mayúsculas y minúsculas.</p><p>Empecemos con el sistema operativo Microsoft Windows 10. Verás lo fácil que es en realidad - solo se requiere un conocimiento básico de GitHub y el símbolo del sistema. Seguro que hay otras maneras de instalar y ejecutar el programa, pero con un conocimiento limitado de codificación sentí que este conjunto de instrucciones era el más fácil de entender y seguir.</p><p>Asegúrese de seguir estos pasos en su totalidad, así como en <strong>el orden correcto (como se indica)</strong> para ahorrarse, tener que desinstalar Go y pasar unas cuantas horas solucionando cualquier problema relacionado con la instalación.</p><h3 id="fase-1-instale-lo-siguiente-en-este-orden">Fase 1: Instale lo siguiente en este orden</h3><ol><li>Como Go utiliza a menudo repositorios de código abierto (¡GRATUITOS!), asegúrate de instalar primero el paquete Git <a href="https://git-scm.com/download/win">aquí</a>.</li><li>Navegue hasta el sitio web de instalación de Go <a href="https://go.dev/doc/install">aquí</a>. Descargue e instale la última versión de Go de 64 bits para el sistema operativo Microsoft Windows.</li><li>Siga las instrucciones del programa de instalación Go.</li><li>Ejecute el símbolo del sistema en su ordenador buscando "cmd". Abra la línea de comandos y escriba "go version"</li><li>La salida después de introducir la versión go debería tener este aspecto:</li></ol><figure class="kg-card kg-image-card"><img src="https://cdn-media-1.freecodecamp.org/images/1*-j7JjyJSN3DqxEdO4lrjTw.png" class="kg-image" alt="1*-j7JjyJSN3DqxEdO4lrjTw" width="493" height="132" loading="lazy"></figure><h3 id="fase-2-creaci-n-del-espacio-de-trabajo-go">Fase 2: Creación del espacio de trabajo Go</h3><p>En primer lugar, confirme sus binarios Go: vaya al Panel de control de su ordenador, luego a Sistema y seguridad &gt; Sistema &gt; Configuración avanzada del sistema y, en el panel de la izquierda, haga clic en la pestaña Avanzado. A continuación, haga clic en Variables de entorno en la parte inferior derecha. Asegúrese de que la variable "C:\Go\bin" está en la ruta de las variables del sistema.</p><p>A continuación, cree su espacio de trabajo Go. Esto será en una carpeta nueva y separada de donde se guardan los archivos de instalación de Go. Por ejemplo, sus archivos de instalación G se guardaron en la ruta C:\Go y usted está creando su espacio de trabajo Go en C:\Projects\Go</p><p>En tu nueva carpeta Go work-space, crea tres nuevas carpetas:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*I3BO4S6FQ6keH6o75ATuBg.png" class="kg-image" alt="1*I3BO4S6FQ6keH6o75ATuBg" width="724" height="128" loading="lazy"><figcaption>bin, pkg, src</figcaption></figure><h3 id="fase-3-crear-la-variable-de-entorno-gopath">Fase 3: Crear la variable de entorno GOPATH</h3><p>Crea la variable GOPATH y haz referencia a tu recién creado espacio de trabajo Go. Vuelve a tu Panel de Control y navega hasta Sistema y luego a Variables de Entorno. A continuación, en Variables del sistema, haga clic en Nuevo.</p><p>Junto a Nombre de variable, introduzca "GOPATH", y junto a Valor de variable introduzca "C:\Proyectos\Go".</p><figure class="kg-card kg-image-card"><img src="https://cdn-media-1.freecodecamp.org/images/1*EdndcOEfhY8DWreAWXjung.png" class="kg-image" alt="1*EdndcOEfhY8DWreAWXjung" width="641" height="129" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://cdn-media-1.freecodecamp.org/images/1*ErNq0vYJQeTJadnJZczBtw.png" class="kg-image" alt="1*ErNq0vYJQeTJadnJZczBtw" width="701" height="189" loading="lazy"></figure><p>Para comprobar que la ruta se ha establecido correctamente, introduzca "echo %GOPATH%" en la línea de comandos.</p><h3 id="fase-4-probar-y-garantizar">Fase 4: Probar y garantizar</h3><p>Ahora estás listo para verificar que todo funciona correctamente abriendo la línea de comandos y escribiendo: <code>go get github.com/golang/example/hello</code></p><p>Espere a que el código se ejecute por completo (puede tardar unos segundos) y, a continuación, introduzca lo siguiente en la línea de comandos: <code>%GOPATH%/bin/hello</code></p><p>Si la instalación se ha realizado correctamente, debería recibir el siguiente mensaje de retorno: “Hello, Go examples!”</p><figure class="kg-card kg-image-card"><img src="https://cdn-media-1.freecodecamp.org/images/1*EXG3IKaDbFqJ3qMpD_n08Q.png" class="kg-image" alt="1*EXG3IKaDbFqJ3qMpD_n08Q" width="559" height="178" loading="lazy"></figure><p>Espero que tenga éxito. Y si te encuentras con algún error o mensajes confusos, comentar a continuación con los resultados de esta línea de comandos: “go env”</p><figure class="kg-card kg-image-card"><img src="https://cdn-media-1.freecodecamp.org/images/1*z6i4jwGkvPE3S21x_PmvMw.png" class="kg-image" alt="1*z6i4jwGkvPE3S21x_PmvMw" width="245" height="300" loading="lazy"></figure><p>¡Y ahora estás listo para convertirte en un "Gopher"!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Guía paso a paso para crear una IA de ajedrez sencilla ]]>
                </title>
                <description>
                    <![CDATA[ Exploremos algunos conceptos básicos que nos ayudarán a crear una IA de ajedrez sencilla:  * move-generation  * board evaluation  * minimax  * and alpha beta pruning. En cada paso, mejoraremos nuestro algoritmo con una de estas técnicas de programación de ajedrez probadas con el tiempo. Demostraré ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/guia-paso-a-paso-para-crear-una-ia-de-ajedrez-sencilla/</link>
                <guid isPermaLink="false">63cbe65f700708073437c12d</guid>
                
                    <category>
                        <![CDATA[ Programación ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jorge Sierra ]]>
                </dc:creator>
                <pubDate>Tue, 24 Jan 2023 19:43:52 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2023/01/1_eP0V-xfRWfW3QHJhALJ5RA.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/simple-chess-ai-step-by-step-1d55a9266977/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">A step-by-step guide to building a simple chess AI</a>
      </p><p>Exploremos algunos conceptos básicos que nos ayudarán a crear una IA de ajedrez sencilla:</p><ul><li>move-generation</li><li>board evaluation</li><li>minimax</li><li>and alpha beta pruning.</li></ul><p>En cada paso, mejoraremos nuestro algoritmo con una de estas técnicas de programación de ajedrez probadas con el tiempo. Demostraré cómo afecta cada una de ellas al estilo de juego del algoritmo.</p><p>Puedes consultar el algoritmo final de IA aquí, en <a href="https://github.com/lhartikk/simple-chess-ai">GitHub</a>.</p><h3 id="paso-1-generaci-n-de-movimientos-y-visualizaci-n-del-tablero">Paso 1: Generación de movimientos y visualización del tablero</h3><p>Usaremos la librería <a href="https://github.com/jhlywa/chess.js">chess.js</a> para la generación de movimientos, y <a href="https://github.com/oakmac/chessboardjs/">chessboard.js</a> para visualizar el tablero. La librería de generación de movimientos básicamente implementa todas las reglas del ajedrez. Basándonos en esto, podemos calcular todos los movimientos legales para un estado dado del tablero.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*_Z_qtrm9ayf_UhycYudE3g.png" class="kg-image" alt="1*_Z_qtrm9ayf_UhycYudE3g" width="800" height="572" loading="lazy"><figcaption>A visualization of the move generation function. The starting position is used as input and the output is all the possible moves from that position.</figcaption></figure><p>El uso de estas bibliotecas nos ayudará a centrarnos solo en la tarea más interesante: crear el algoritmo que encuentre la mejor jugada.</p><p>Empezaremos creando una función que simplemente devuelva una jugada aleatoria de entre todas las posibles:</p><p>Aunque este algoritmo no es un jugador de ajedrez muy sólido, es un buen punto de partida, ya que podemos jugar contra él:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*GzOiJRh6Z3FOC3xmPEmKrQ.gif" class="kg-image" alt="1*GzOiJRh6Z3FOC3xmPEmKrQ" width="410" height="402" loading="lazy"><figcaption>Black plays random moves. Playable on <a href="https://jsfiddle.net/lhartikk/m14epfwb/" rel="noopener" target="_blank" title="" 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-size: 17.6px; vertical-align: baseline; background-color: transparent; color: var(--gray90); text-decoration: underline; cursor: pointer; word-break: break-word;">https://jsfiddle.net/lhartikk/m14epfwb/</a>4</figcaption></figure><h3 id="paso-2-evaluaci-n-de-la-posici-n">Paso 2: Evaluación de la posición</h3><p>Ahora vamos a intentar comprender qué bando es más fuerte en una posición determinada. La forma más sencilla de conseguirlo es contar la fuerza relativa de las piezas en el tablero utilizando la siguiente tabla:</p><figure class="kg-card kg-image-card"><img src="https://cdn-media-1.freecodecamp.org/images/1*e4p9BrCzJUdlqx7KVGW9aA.png" class="kg-image" alt="1*e4p9BrCzJUdlqx7KVGW9aA" width="413" height="488" loading="lazy"></figure><p>Con la función de evaluación, somos capaces de crear un algoritmo que elige el movimiento que da la evaluación más alta:</p><p>La única mejora tangible es que ahora nuestro algoritmo capturará una pieza si puede.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*fTWDdJ2m3L72X6rqce9_tQ.gif" class="kg-image" alt="1*fTWDdJ2m3L72X6rqce9_tQ" width="410" height="402" loading="lazy"><figcaption>Black plays with the aid of the simple evaluation function. Playable on <a href="https://jsfiddle.net/lhartikk/m5q6fgtb/1/" rel="noopener" target="_blank" title="" 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-size: 17.6px; vertical-align: baseline; background-color: transparent; color: var(--gray90); text-decoration: underline; cursor: pointer; word-break: break-word;">https://jsfiddle.net/lhartikk/m5q6fgtb/1/</a></figcaption></figure><h3 id="paso-3-rbol-de-b-squeda-utilizando-minimax">Paso 3: Árbol de búsqueda utilizando Minimax</h3><p>A continuación vamos a crear un árbol de búsqueda a partir del cual el algoritmo pueda elegir la mejor jugada. Esto se hace mediante el algoritmo <a href="https://es.wikipedia.org/wiki/Minimax">Minimax</a>.</p><p>En este algoritmo, el árbol recursivo de todos los movimientos posibles se explora hasta una profundidad determinada, y la posición se evalúa en las "hojas" finales del árbol.</p><p>Después de eso, devolvemos el menor o el mayor valor del hijo al nodo padre, dependiendo de si es un blanco o un negro para mover. (Es decir, tratamos de minimizar o maximizar el resultado en cada nivel).</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*UA5VlNs7s4gl80VknA099w.jpeg" class="kg-image" alt="1*UA5VlNs7s4gl80VknA099w" width="791" height="312" loading="lazy"><figcaption>A visualization of the minimax algorithm in an artificial position. The best move for white is <strong style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 700; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 17.6px; vertical-align: baseline; color: var(--gray85);">b2-c3</strong>, because we can guarantee that we can get to a position where the evaluation is <strong style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 700; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 17.6px; vertical-align: baseline; color: var(--gray85);">-50</strong></figcaption></figure><p>Con minimax en su lugar, nuestro algoritmo está empezando a entender algunas tácticas básicas del ajedrez:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*xRfitY19MvJW3ynGKWhQ5A.gif" class="kg-image" alt="1*xRfitY19MvJW3ynGKWhQ5A" width="396" height="398" loading="lazy"><figcaption>Minimax with depth level 2. Playable on: <a href="https://jsfiddle.net/k96eoq0q/1/" rel="noopener" target="_blank" title="" 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-size: 17.6px; vertical-align: baseline; background-color: transparent; color: var(--gray90); text-decoration: underline; cursor: pointer; word-break: break-word;">https://jsfiddle.net/k96eoq0q/1/</a></figcaption></figure><p>La eficacia del algoritmo minimax se basa en gran medida en la profundidad de búsqueda que podamos alcanzar. Esto es algo que mejoraremos en el siguiente paso.</p><h3 id="paso-4-poda-alfa-beta">Paso 4: Poda alfa-beta</h3><p>La poda <a href="https://es.wikipedia.org/wiki/Poda_alfa-beta">alfa-beta</a> es un método de optimización del algoritmo minimax que nos permite descartar algunas ramas del árbol de búsqueda. Esto nos ayuda a evaluar el árbol de búsqueda minimax mucho más profundamente, utilizando los mismos recursos.</p><p>La poda alfa-beta se basa en la situación en la que podemos dejar de evaluar una parte del árbol de búsqueda si encontramos un movimiento que conduce a una situación peor que un movimiento descubierto anteriormente.</p><p>La poda alfa-beta no influye en el resultado del algoritmo minimax, sólo lo hace más rápido.</p><p>El algoritmo alfa-beta también es más eficiente si visitamos primero los caminos que conducen a buenas jugadas.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*96QEzhnsOkNqz7swB0qx8w.jpeg" class="kg-image" alt="1*96QEzhnsOkNqz7swB0qx8w" width="791" height="312" loading="lazy"><figcaption>The positions we do not need to explore if alpha-beta pruning isused and the tree is visited in the described order.</figcaption></figure><p>Con alfa-beta, obtenemos una mejora significativa del algoritmo minimax, como se muestra en el siguiente ejemplo:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*k3DrkWLNq33ei_t-094qpg.png" class="kg-image" alt="1*k3DrkWLNq33ei_t-094qpg" width="393" height="471" loading="lazy"><figcaption>The number of positions that are required to evaluate if we want to perform a search with depth of 4 and the “root” position is the one that is shown.</figcaption></figure><p>Sigue este <a href="https://jsfiddle.net/Laa0p1mh/3/">enlace </a>para probar la versión alfa-beta mejorada de la IA de ajedrez.</p><h3 id="paso-5-mejora-de-la-funci-n-de-evaluaci-n">Paso 5: Mejora de la función de evaluación</h3><p>La función de evaluación inicial es bastante ingenua, ya que sólo contamos el material que se encuentra en el tablero. Para mejorarla, añadimos a la evaluación un factor que tiene en cuenta la posición de las piezas. Por ejemplo, un caballo en el centro del tablero es mejor (porque tiene más opciones y, por tanto, es más activo) que un caballo en el borde del tablero.</p><p>Utilizaremos una versión ligeramente ajustada de las tablas pieza-cuadrado que se describen originalmente en <a href="https://chessprogramming.wikispaces.com/Simplified+evaluation+function">chess-programming-wiki.</a></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*iG6FUYZpU0_RKlqHnC8XxA.png" class="kg-image" alt="1*iG6FUYZpU0_RKlqHnC8XxA" width="800" height="782" loading="lazy"><figcaption>The visualized piece-square tables visualized. We can decrease or increase the evaluation, depending on the location of the piece.</figcaption></figure><p>Con la siguiente mejora, empezamos a conseguir un algoritmo que juega algo de ajedrez "decente", al menos desde el punto de vista de un jugador ocasional:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*sX_XwfPrOQ6c62iuVZ75fw.gif" class="kg-image" alt="1*sX_XwfPrOQ6c62iuVZ75fw" width="396" height="398" loading="lazy"><figcaption>Improved evaluation and alpha-beta pruning with search depth of 3. Playable on <a href="https://jsfiddle.net/q76uzxwe/1/" rel="noopener" target="_blank" title="" 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-size: 17.6px; vertical-align: baseline; background-color: transparent; color: var(--gray90); text-decoration: underline; cursor: pointer; word-break: break-word;">https://jsfiddle.net/q76uzxwe/1/</a></figcaption></figure><h3 id="conclusiones">Conclusiones</h3><p>El punto fuerte de un algoritmo de ajedrez sencillo es que no comete errores estúpidos. Dicho esto, le sigue faltando comprensión estratégica.</p><p>Con los métodos que he introducido aquí, hemos sido capaces de programar un algoritmo de juego de ajedrez que puede jugar al ajedrez básico. La "parte IA" (excluyendo la generación de jugadas) del algoritmo final tiene sólo 200 líneas de código, lo que significa que los conceptos básicos son bastante sencillos de implementar. Puede consultar la versión final en <a href="https://github.com/lhartikk/simple-chess-ai" rel="noopener">GitHub</a>.</p><p>Otras mejoras que podríamos introducir en el algoritmo serían, por ejemplo:</p><ul><li>move-ordering</li><li>generación de movimientos más rápidos</li><li>y la evaluación específica de los objetivos finales.</li></ul><p>Si quieres aprender más, echa un vistazo a la wiki de programación de ajedrez. Es un recurso útil para explorar más allá de estos conceptos básicos que he introducido aquí.</p><p>Gracias por leerme.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo testear los componentes de React: La guía completa ]]>
                </title>
                <description>
                    <![CDATA[ Cuando comencé a aprender a probar mis aplicaciones en el pasado, me frustró mucho con los diferentes tipos, estilos y tecnologías utilizadas para las pruebas, junto con la variedad disuelta de publicaciones de blog, tutoriales y artículos. Descubrí que esto también es cierto para las pruebas de React. Así que ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-probar-los-componentes-de-react-la-guia-completa/</link>
                <guid isPermaLink="false">630964a149da500911a0fcec</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jorge Sierra ]]>
                </dc:creator>
                <pubDate>Mon, 28 Nov 2022 19:14:37 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2022/08/photo-1562679817-2aac4f5581ec.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/testing-react-hooks/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Test React Components: the Complete Guide</a>
      </p><p>Cuando comencé a aprender a probar mis aplicaciones en el pasado, me frustró mucho con los diferentes tipos, estilos y tecnologías utilizadas para las pruebas, junto con la variedad disuelta de publicaciones de blog, tutoriales y artículos. Descubrí que esto también es cierto para las pruebas de React.</p><p>Así que decidí escribir una guía completa de prueba de React en un artículo.</p><p>Guía completa, eh, ¿va a cubrir todos los escenarios de prueba posibles? Por supuesto que no. Sin embargo, será una guía básica completa para las pruebas y será suficiente para desarrollar la mayoría de los otros casos extremos.</p><p>También seleccioné una extensa colección de publicaciones de blog, artículos y tutoriales en la sección de lectura adicional al final que debería brindarle el conocimiento suficiente para estar en el 10% superior de los desarrolladores en términos de pruebas.</p><p>Puedes encontrar el proyecto terminado aquí:<br><a href="https://github.com/iqbal125/react-hooks-testing-complete">https://github.com/iqbal125/react-hooks-testing-complete</a></p><h2 id="tabla-de-contenido">tabla de contenido</h2><h3 id="teor-a">teoría</h3><ul><li>¿Qué son los Test?</li><li>¿Por qué testear?</li><li>¿Qué testear?</li><li>¿Qué no testear?</li><li>como Testeo</li><li>Superficial vs Profundo</li><li>unidad vs integracion vs eae</li></ul><h3 id="informacion-preliminar">informacion preliminar</h3><ul><li>algunas probabilidades y terminacion</li></ul><h3 id="enzima">enzima</h3><ul><li>Configuración de Enyme</li><li>renderizador de prueba de reaccion</li><li>prueba de instantaneas</li><li>probar los detalles de implementacion</li></ul><h3 id="biblioteca-de-pruebas-react">Biblioteca de pruebas React </h3><ul><li>useState y props</li><li>usarReductor()</li><li>useContext()</li><li>Formularios de componentes controlados</li><li>Solicitudes de API de useEffect() y Axios</li></ul><p><strong><strong>Ciprés</strong></strong></p><ul><li>Una prueba completa de extremo a extremo</li></ul><h3 id="integracion-continua">integracion continua</h3><ul><li>Travis.yml</li><li>Código Cobertura con overoles</li></ul><h2 id="teoria"><strong>teoria</strong></h2><h3 id="-que-es-el-testin"><strong>¿Que es el Testin?</strong></h3><p>Comencemos por el principio y analicemos qué es la prueba. La prueba es un proceso de 3 pasos que se ve asi:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/07/image-12.png" class="kg-image" alt="image-12" width="600" height="400" loading="lazy"></figure><p>Arreglar, su aplicación está en un cierto estado original. Actúa, luego sucede algo (evento de clic, entrada, etc.). Luego afirma, o hace una hipótesis, del nuevo estado de su aplicación. Las pruebas pasarán si su hipótesis es correcta y fallarán si es incorrecta.</p><p>A diferencia de sus componentes de reacción, sus pruebas no se ejecutan en el navegador. Jest es el corredor de pruebas y el marco de prueba utilizado por React. Jest es el entorno donde se ejecuta realmente todas las pruebas. Es por eso que no necesita importar <code>expect</code>y <code>describe</code>en sus archivos. Estas funciones ya están disponibles globalmente en el entorno jest.</p><p>La sintaxis de sus pruebas se verá así:</p><pre><code class="language-javascript">describe('Testing sum', () =&gt; {
    function sum(a, b) {
       return a + b;
    }

    it('should equal 4',()=&gt;{
       expect(sum(2,2)).toBe(4);
      })

    test('also should equal 4', () =&gt; {
        expect(sum(2,2)).toBe(4);
      }) 
});</code></pre><p><code>describe</code> envuelve nuestro bloque <code>it</code>o <code>test</code>, y es una forma de agrupar nuestras pruebas. Ambas <code>it</code>y <code>test</code>son palabras clave y se pueden usar indistintamente. La cadena será algo que debería suceder con sus pruebas y se imprimirá en la consola. <code>toBe()</code>es un comparador que funciona con esperar para permitirle hacer afirmaciones. Hay muchos más emparejadores y variables globales que ofrecemos broma, vea los enlaces a continuación para obtener una lista completa.</p><p><a href="https://jestjs.io/docs/en/using-matchers">https://jestjs.io/docs/en/using-matchers</a></p><p><a href="https://jestjs.io/docs/en/api">https://jestjs.io/docs/en/api</a></p><h3 id="-qu-testear">¿Qué <strong>testear? </strong></h3><p>Los test se realizan para garantizar que su aplicación funcione según lo previsto para sus usuarios finales. Tener pruebas hará que su aplicación sea más robusta y menos propensa a errores. Es una forma de verificar que el código está haciendo lo que pretendían los desarrolladores.</p><p>Posibles desventajas:</p><ul><li>La redacción de la prueba requiere mucho tiempo y es difícil.</li><li>En ciertos escenarios, ejecutar test en CI puede costar dinero real.</li><li>Si se hace incorrectamente, puede darte falsos positivos. Sus pruebas pasan, pero su aplicación no funciona según lo previsto.</li><li>O falsos negativos. Sus pruebas fallan, pero su aplicación funciona según lo previsto.</li></ul><h3 id="-qu-testear-1">¿Qué testear?</h3><p>Para desarrollar el punto anterior, sus pruebas deben probar la funcionalidad de la aplicación, que imita cómo la usarán sus usuarios finales. Esto le dará la confianza de que su aplicación funcionará según lo previsto en su entorno de producción. Por supuesto, entraremos en muchos más detalles a lo largo de este artículo, pero esta es la esencia básica.</p><h3 id="-qu-no-testear">¿Qué no testear?</h3><p>Me gusta usar la filosofía de Kent C dodds aquí de que no debe probar los detalles de implementación.</p><p>Detalles de implementación que significan probar cosas que no son la funcionalidad del usuario final. Veremos un ejemplo de esto en la sección Enzima a continuación.</p><p>Parece que está probando la funcionalidad allí, pero en realidad no es así. Estás probando el nombre de la función. Porque puede cambiar el nombre de la función y sus pruebas fallarán, pero su aplicación seguirá funcionando y le dará un falso negativo.</p><p>Tener que preocuparse constantemente por los nombres de funciones y variables es un dolor de cabeza, y tener que reescribir las pruebas cada vez que las cambia es tedioso, le mostraré un mejor enfoque.</p><p><strong><strong>Const variables:</strong></strong> estas son variables que no cambian, no es necesario probarlas.</p><p><strong>Bibliotecas de terceros<strong>:</strong></strong> No es su trabajo probar estas bibliotecas. Depende de los creadores de estas bibliotecas probarlo. Si no está seguro de si una biblioteca está probada, no debe usarla. O puede leer el código fuente para ver si el autor incluye pruebas. Puede descargar el código fuente y ejecutar estas pruebas usted mismo. También puede preguntarle al autor si su biblioteca está lista para la producción o no. &nbsp;</p><h3 id="mi-filosof-a-personal-sobre-las-pruebas">Mi filosofía personal sobre las pruebas</h3><p>Gran parte de mi filosofía de evaluación se basa en las enseñanzas de Kent C dodds, por lo que verás muchos de sus sentimientos reflejados aquí, pero también algunos de mis propios pensamientos.</p><p>Muchas pruebas de integración. Sin pruebas instantáneas. Pocas pruebas unitarias. Pocas pruebas de ea e.</p><p>Las pruebas unitarias están un paso por encima de las pruebas instantáneas, pero no son ideales. Sin embargo, es mucho más fácil de entender y mantener que las pruebas instantáneas.</p><p>Escribir principalmente pruebas de integración. Las pruebas unitarias son buenas, pero en realidad no se parecen a la forma en que su usuario final interactúa con su aplicación. Es muy fácil probar los detalles de implementación con pruebas unitarias, especialmente con renderizado superficial.</p><p>Las pruebas de integración deben usarse lo menos posible</p><p>No se probaron detalles de implementación como nombres de funciones y variables.</p><p>Por ejemplo, si estamos probando un botón y cambiamos el nombre de la función en el método onClick de increment() a handleClick(), nuestras pruebas fallarían pero nuestro componente seguirá funcionando. Esta es una mala práctica porque básicamente solo estamos probando el nombre de la función, que es un detalle de implementación, que no le importa a nuestro usuario final.</p><h3 id="montaje-superficial-vs-"><strong>Montaje superficial vs.</strong></h3><p>Mount en realidad ejecuta el código html, css y js como lo haría un navegador, pero lo hace de forma simulada. Es "sin cabeza", por ejemplo, lo que significa que no representa ni pinta nada en una interfaz de usuario, sino que actúa como un navegador web simulado y ejecuta el código en segundo plano.</p><p>No perder tiempo pintando nada en la interfaz de usuario hace que sus pruebas sean mucho más rápidas. Sin embargo, las pruebas de montaje siguen siendo mucho más lentas que las pruebas superficiales.</p><p>Esta es la razón por la que desmonta o limpia el componente después de cada prueba, porque es casi una aplicación en vivo y una prueba afectará a otra prueba.</p><p>Mount/render se usa normalmente para prueba de integración y superficial para prueba unitaria.</p><p>la renderización superficial solo renderiza el único componente que estamos probando. No renderiza componentes secundarios. Esto nos permite probar nuestro componente de forma aislada.</p><p>Por ejemplo, considere este componente hijo y padre.</p><pre><code class="language-javascript">import React from 'react';

const App = () =&gt; {
  return (
    &lt;div&gt; 
      &lt;ChildComponent /&gt; 
    &lt;/div&gt; 
  )
}

const ChildComponent = () =&gt; {
  return (
    &lt;div&gt;
     &lt;p&gt; Child components&lt;/p&gt;
    &lt;/div&gt;
  )
}</code></pre><p>Si usamos una representación superficial de <code>App.js</code>obtendríamos algo como esto, observe que ninguno de los nodos DOM para el componente secundario está presente, de ahí el término representación superficial.</p><pre><code>&lt;App&gt;
  &lt;div&gt; 
    &lt;ChildComponent /&gt; 
  &lt;/div&gt;
&lt;/App&gt; </code></pre><p>Ahora podemos comparar esto con el montaje del componente:</p><pre><code>&lt;App&gt;
  &lt;div&gt; 
    &lt;ChildComponent&gt; 
      &lt;div&gt;
       &lt;p&gt; Child components&lt;/p&gt;
      &lt;/div&gt;
    &lt;/ChildComponent&gt;
   &lt;/div&gt;
&lt;/App&gt; </code></pre><p>Lo que tenemos arriba está mucho más cerca de cómo se verá nuestra aplicación en el navegador, de ahí la superioridad de mount/render.</p><h3 id="unidad-vs-integraci-n-vs-extremo-a-extremo"><strong>Unidad vs integración vs extremo a extremo</strong></h3><p><strong><strong>unit testing</strong></strong> : probando una parte aislada de su aplicación, generalmente realizada en combinación con una representación superficial. Ejemplo: un componente se renderiza con los accesorios predeterminados.</p><p><strong><strong>pruebas de integración: </strong></strong>probar si las diferentes partes funcionan o se integran entre sí. Por lo general, se realiza con el montaje o renderizado de un componente. Ejemplo: prueba si un componente secundario puede actualizar el estado del contexto en un componente principal.</p><p><strong><strong>e to e testing</strong></strong> : significa "de extremo a extremo". Por lo general, una prueba de varios pasos que combina múltiples pruebas unitarias y de integración en una gran prueba. Por lo general, muy poco se burla o se critica. Las pruebas se realizan en un navegador simulado, puede haber o no una interfaz de usuario mientras se ejecuta la prueba. Ejemplo: probar un flujo de autenticación completo.</p><h2 id="informacion-preliminar-1">Informacion preliminar</h2><p><strong><strong>react-testing-library:</strong></strong> Personalmente, me gusta usar react-testing-library, pero la forma común es usar Enzyme. Le mostraré un ejemplo de Enzyme porque es importante conocer Enzyme en un nivel básico y el resto de los ejemplos con react-testing-library.</p><p><strong>Esquema de ejemplos<strong>:</strong></strong> Nuestros ejemplos seguirán un patrón. Primero le mostraré el componente React y luego las pruebas para él, con detalles detallados de cada uno. También puede seguir el repositorio vinculado al principio.</p><p><strong>Configuración<strong>:</strong></strong> También supondré que está utilizando create-react-app con la configuración de prueba predeterminada con jest, por lo que omitiré las configuraciones manuales.</p><p><strong>Sinon, moca, chai<strong>:</strong></strong> Gran parte de la funcionalidad que ofrece sinon está disponible de forma predeterminada con jest, por lo que no necesita sinon. Mocha y chai son un reemplazo para la broma. Jest viene preconfigurado de fábrica para funcionar con su aplicación, por lo que no tiene sentido usar Mocha y chai.</p><p><strong>Esquema de nomenclatura de componentes<strong>:</strong></strong> Mi esquema de nombres para los componentes es <code>&lt;TestSomething /&gt;</code> pero eso no significa que sean componentes falsos de ninguna manera. Son componentes regulares de React, este es solo el esquema de nombres.</p><p><strong><strong>npm test y jest watch mode</strong></strong> :<code>yarn test</code> &nbsp;trabajó para mi. <code>npm test</code>no funcionaba correctamente con el modo de reloj de broma.</p><p><strong>probando un solo archivo <strong>:</strong></strong> <code>yarn test</code> nombre del archivo</p><p><strong><strong>React Hooks vs Classes:</strong></strong> Utilizo los componentes de React Hooks para la mayoría de los ejemplos, pero debido al poder de la biblioteca de pruebas de react, todas estas pruebas también funcionarán directamente con los componentes de la clase.</p><p>Con la información de fondo preliminar fuera del camino, podemos reparar algo de código.</p><h2 id="enzima-1"><strong>enzima</strong></h2><h3 id="configuraci-n-de-enzimas">Configuración <strong>de enzimas</strong></h3><p>Nuestras bibliotecas de terceros</p><p><code>npm install enzyme enzyme-to-json &nbsp;enzyme-adapter-react-16</code></p><p>Comenzamos primero con nuestras importaciones.</p><pre><code class="language-javascript">import React from 'react';
import ReactDOM from 'react-dom';
import Basic from '../basic_test';

import Enzyme, { shallow, render, mount } from 'enzyme';
import toJson from 'enzyme-to-json';
import Adapter from 'enzyme-adapter-react-16';

Enzyme.configure({ adapter: new Adapter() })</code></pre><p>Comenzaremos con nuestras importaciones básicas. Nuestras primeras 3 importaciones son para reaccionar y nuestros componentes.</p><p>Después de esto importamos Enzyme. Luego importamos la función toJson de la biblioteca 'enzyme-to-json'. Necesitaremos esto para convertir nuestros componentes renderizados poco profundos en JSON, que se pueden guardar en el archivo de forma instantánea.</p><p>Finalmente, importamos nuestro Adaptador para hacer que la enzima funcione con React 16 y lo inicializamos como se muestra arriba.</p><p><br><strong><strong>react-test-renderer</strong></strong></p><p>React en realidad viene con su propio renderizador de prueba que puede usar en lugar de enzima y la sintaxis se verá así.</p><pre><code class="language-javascript">// import TestRenderer from 'react-test-renderer';
// import ShallowRenderer from 'react-test-renderer/shallow';


// Basic Test with React-test-renderer
// it('renders correctly react-test-renderer', () =&gt; {
//   const renderer = new ShallowRenderer();
//   renderer.render(&lt;Basic /&gt;);
//   const result = renderer.getRenderOutput();
//
//   expect(result).toMatchSnapshot();
// });</code></pre><p>Pero incluso los documentos de react-test-render sugieren usar enzima en su lugar porque tiene una sintaxis un poco más agradable y hace lo mismo. Sólo algo a tener en cuenta. </p><h3 id="prueba-de-instantaneas">Prueba de instantaneas</h3><p>Ahora nuestra primera prueba, que es una prueba instantánea.</p><pre><code class="language-javascript">it('renders correctly enzyme', () =&gt; {
  const wrapper = shallow(&lt;Basic /&gt;)

  expect(toJson(wrapper)).toMatchSnapshot();
});</code></pre><p>Si no ha ejecutado este comando antes, se creará automáticamente una carpeta <code>_snapshots_</code> y un archivo test.js.snap. En cada prueba posterior, la nueva instantánea se comparará con el archivo de instantánea existente. La prueba pasará si la instantánea no ha cambiado y fallará si ha cambiado.</p><p>Básicamente, las pruebas instantáneas le permiten ver cómo ha cambiado su componente desde la última prueba, línea por línea. Las líneas de código que han cambiado se conocen como diff.</p><p>Aquí está nuestro componente básico que estamos probando instantáneas:</p><pre><code>import React from 'react';


const Basic = () =&gt; {
  return (
    &lt;div &gt;
      &lt;h1&gt; Basic Test&lt;/h1&gt;
         &lt;p&gt; This is a basic Test Component&lt;/p&gt;
    &lt;/div&gt;
  );
}

export default Basic;</code></pre><p><br>Ejecutar la prueba anterior generará un archivo que se verá así. Este es esencialmente nuestro árbol de nodos React DOM.</p><pre><code>// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly enzyme 1`] = `
&lt;div&gt;
  &lt;h1&gt;
     Basic Test
  &lt;/h1&gt;
  &lt;p&gt;
     This is a basic Test Component
  &lt;/p&gt;
&lt;/div&gt;
`;</code></pre><p>Y producirá una estructura de carpetas que se verá así:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/07/image-6.png" class="kg-image" alt="image-6" width="600" height="400" loading="lazy"></figure><p><br>La salida de tu terminal se verá así:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/07/image-7.png" class="kg-image" alt="image-7" width="600" height="400" loading="lazy"></figure><p>Sin embargo, ¿qué sucede si cambiamos nuestro componente básico a este</p><pre><code>import React from 'react';


const Basic = () =&gt; {
  return (
    &lt;div &gt;
      &lt;h1&gt; Basic Test&lt;/h1&gt;

    &lt;/div&gt;
  );
}

export default Basic;</code></pre><p>Nuestras instantáneas ahora fallarán</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/07/image-8.png" class="kg-image" alt="image-8" width="600" height="400" loading="lazy"></figure><p>Y también nos dará la diferencia.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/07/image-9.png" class="kg-image" alt="image-9" width="600" height="400" loading="lazy"></figure><p>Al igual que en git, el "-" antes de cada línea significa que se eliminó.</p><p>Solo necesitamos presionar "w" para activar el modo reloj y luego presionar "u" para actualizar la instantánea.</p><p>Nuestro archivo de instantáneas se actualizará automáticamente con la nueva instantánea y pasará nuestras pruebas</p><pre><code>// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly enzyme 1`] = `
&lt;div&gt;
  &lt;h1&gt;
     Basic Test
  &lt;/h1&gt;
&lt;/div&gt;
`;</code></pre><p><br>Esto es solo para pruebas de instantáneas, pero si lees mi sección de pensamientos personales, sabes que no hago pruebas de instantáneas. Lo incluí aquí porque, al igual que Enzyme, es muy común y es algo que debe tener en cuenta, pero a continuación intentaré explicar por qué no lo uso.</p><p>Repasemos de nuevo qué es la prueba de instantáneas. Básicamente, le permite ver cómo ha cambiado su componente desde la última prueba. Cuáles son los beneficios de esto.</p><ul><li>Es muy rápido y fácil de implementar y, a veces, solo requiere unas pocas líneas de código.</li><li>Puedes ver si nuestro componente se está renderizando correctamente. Puedes ver claramente los nodos DOM con la función .debug().</li></ul><p><strong>Contras, Argumentos en contra de las pruebas de instantáneas:</strong></p><ul><li>Lo único que hace una prueba instantánea es decirle si la sintaxis de su código ha cambiado desde la última prueba.</li><li>Entonces, ¿qué es lo que realmente está probando? Algunos dirían que no mucho.</li><li>También la representación básica de la aplicación correctamente es el trabajo de React, por lo que vas a probar un territorio de biblioteca de terceros.</li><li>También se pueden comparar diferencias con el control de versiones de git. Este no debería ser el trabajo de las pruebas de instantáneas.</li><li>Una prueba fallida no significa que su aplicación no esté funcionando según lo previsto, solo que su código ha cambiado desde la última vez que ejecutó la prueba. Esto puede generar muchos falsos negativos y una falta de confianza en la prueba. Esto también puede llevar a que las personas simplemente actualicen la prueba sin mirarla demasiado de cerca.</li><li>La prueba de instantáneas también le dice si su JSX es sintácticamente correcto, pero nuevamente, esto se puede hacer fácilmente en el entorno de desarrollo. Ejecutar una prueba de instantánea solo para verificar los errores de sintaxis no tiene ningún sentido.</li><li>Puede ser difícil entender lo que sucede en una prueba de instantáneas, ya que la mayoría de las personas usan pruebas de instantáneas con renderizado superficial, que no renderiza componentes secundarios, por lo que no le da al desarrollador ningún conocimiento.</li></ul><p>Consulta la sección de lectura adicional para obtener más información.</p><h3 id="probando-los-detalles-de-implementaci-n-con-enzyme">Probando los detalles de implementación con Enzyme</h3><p>Aquí daré un ejemplo de por qué no probar los detalles de implementación. Digamos que tenemos un componente de contador simple así:</p><pre><code class="language-javascript">import React, { Component } from 'react';


class Counter extends Component {
  constructor(props) {
    super(props)

    this.state = {
      count: 0
    }
  }

  increment = () =&gt; {
    this.setState({count: this.state.count + 1})
  }

  //This incorrect code will still cause tests to pass
  // &lt;button onClick={this.incremen}&gt;
  //   Clicked: {this.state.count}
  // &lt;/button&gt;

  render() {
    return (
      &lt;div&gt;
        &lt;button className="counter-button" onClick={this.incremen}&gt;
          Clicked: {this.state.count}
        &lt;/button&gt;
      &lt;/div&gt;
  )}
}

export default Counter;</code></pre><p>Notarás que tengo un comentario que sugiere que una aplicación que no funciona hará que las pruebas pasen, por ejemplo, al escribir mal el nombre de la función en el evento onClick.</p><p>Y veamos las pruebas que dejarán claro por qué.</p><pre><code class="language-javascript">import React from 'react';
import ReactDOM from 'react-dom';
import Counter from '../counter';

import Enzyme, { shallow, render, mount } from 'enzyme';
import toJson from 'enzyme-to-json';
import Adapter from 'enzyme-adapter-react-16';

Enzyme.configure({ adapter: new Adapter() })

// incorrect function assignment in the onClick method
// will still pass the tests.

test('the increment method increments count', () =&gt; {
  const wrapper = mount(&lt;Counter /&gt;)

  expect(wrapper.instance().state.count).toBe(0)

  // wrapper.find('button.counter-button').simulate('click')
  // wrapper.setState({count: 1})
  wrapper.instance().increment()
  expect(wrapper.instance().state.count).toBe(1)
})
</code></pre><p>Ejecutando el código anterior pasará las pruebas. También lo hará el uso <code>wrapper.setState()</code>. Así que tenemos pruebas de aprobación con una aplicación no funcional. No sé sobre usted, pero esto no me da confianza de que nuestra aplicación funcionará según lo previsto para nuestros usuarios finales.</p><p>Simular hacer clic en el botón no pasará las pruebas, pero podría darnos el problema opuesto, un falso negativo. Digamos que queremos cambiar el estilo del botón declarando una nueva clase CSS para él, una situación muy común. Nuestras pruebas ahora fallarán porque ya no podemos encontrar nuestro botón, pero nuestra aplicación seguirá funcionando, dándonos un falso negativo. Esto también es cierto cada vez que cambiamos los nombres de nuestras funciones o variables de estado.</p><p>Cada vez que queremos cambiar nuestra función y los nombres de clase CSS tenemos que reescribir nuestras pruebas, un proceso muy ineficiente y tedioso.</p><p>Entonces, ¿qué podemos hacer en su lugar?</p><h2 id="react-testing-library"><strong>React-testing-library</strong></h2><h3 id="usestate"><strong>useState</strong></h3><p>De los documentos de la biblioteca de pruebas de react vemos que el principio rector principal es</p><blockquote>Cuanto más se parezcan sus pruebas a la forma en que se usa su software, más confianza le pueden brindar.</blockquote><p>Tendremos en cuenta este principio rector a medida que exploremos más con nuestras pruebas.</p><p>Comencemos con un componente básico de React Hooks y probemos el estado y las propiedades.</p><pre><code class="language-javascript">import React, { useState } from 'react';


const TestHook = (props) =&gt; {
  const [state, setState] = useState("Initial State")

  const changeState = () =&gt; {
    setState("Initial State Changed")
  }

  const changeNameToSteve = () =&gt; {
    props.changeName()
  }

  return (
  &lt;div&gt;
    &lt;button onClick={changeState}&gt;
      State Change Button
    &lt;/button&gt;
    &lt;p&gt;{state}&lt;/p&gt;
    &lt;button onClick={changeNameToSteve}&gt;
       Change Name
    &lt;/button&gt;
    &lt;p&gt;{props.name}&lt;/p&gt;
  &lt;/div&gt;
  )
}


export default TestHook;</code></pre><p>Nuestros accesorios provienen del componente principal root</p><pre><code class="language-javascript">  const App = () =&gt; {
      const [state, setState] = useState("Some Text")
      const [name, setName] = useState("Moe")
  ...
      const changeName = () =&gt; {
        setName("Steve")
      }

      return (
        &lt;div className="App"&gt;
         &lt;Basic /&gt;
        &lt;h1&gt; Counter &lt;/h1&gt;
         &lt;Counter /&gt;
        &lt;h1&gt; Basic Hook useState &lt;/h1&gt;
         &lt;TestHook name={name} changeName={changeName}/&gt;
    ...     </code></pre><p>Entonces, teniendo en cuenta nuestro principio rector, ¿cómo serán nuestras pruebas?</p><p>La forma en que nuestro usuario final usará esta aplicación será: ver un texto en la interfaz de usuario, ver el texto en el botón, luego hacer clic en él, finalmente ver un texto nuevo en la interfaz de usuario.</p><p>Así es como escribiremos nuestras pruebas usando la biblioteca de pruebas de React.</p><p>Utiliza este comando para instalar la biblioteca de pruebas de react.</p><p><code>npm install @testing-library/react</code></p><p><strong><strong>no</strong></strong></p><p><code>npm install react-testing-library</code></p><p>Ahora para nuestras pruebas</p><pre><code class="language-javascript">import React from 'react';
import ReactDOM from 'react-dom';
import TestHook from '../test_hook.js';
import {render, fireEvent, cleanup} from '@testing-library/react';
import App from '../../../App'

afterEach(cleanup)

it('Text in state is changed when button clicked', () =&gt; {
    const { getByText } = render(&lt;TestHook /&gt;);

    expect(getByText(/Initial/i).textContent).toBe("Initial State")

    fireEvent.click(getByText("State Change Button"))

    expect(getByText(/Initial/i).textContent).toBe("Initial State Changed")
 })


it('button click changes props', () =&gt; {
  const { getByText } = render(&lt;App&gt;
                                &lt;TestHook /&gt;
                               &lt;/App&gt;)

  expect(getByText(/Moe/i).textContent).toBe("Moe")

  fireEvent.click(getByText("Change Name"))

  expect(getByText(/Steve/i).textContent).toBe("Steve")
})</code></pre><p>Primero comenzamos con nuestras importaciones habituales.</p><p>A continuación tenemos la funcion <code>afterEach(cleanup)</code>. Dado que no estamos usando renderizado superficial, tenemos que desmontar o limpiar después de cada prueba. Y esto es exactamente lo que está haciendo esta función.</p><p><code>getByText</code> es el método de consulta que obtenemos al usar la desestructuración de objetos en el valor de la función de representación. Hay varios métodos de consulta más, pero este es el que querrá usar la mayor parte del tiempo.</p><p>Para probar nuestro aviso de estado, no estamos usando ningún nombre de función o los nombres de nuestras variables de estado. Mantenemos nuestro principio rector y no probamos los detalles de implementación. Dado que un usuario verá el texto en la interfaz de usuario, así es como consultaremos los nodos DOM. También consultaremos el botón de esta manera y haremos clic en él. Finalmente, también consultaremos el estado final en función del texto.</p><p><code>(/Initial/i)</code> es una expresión regular que devuelve el primer nodo que al menos contiene el texto "Inicial".</p><p>es una expresión regular que devuelve el primer nodo que al menos contiene el texto "Inicial". <code>App.js</code> necesitaremos renderizarlo junto con nuestro componente. Al igual que en el ejemplo anterior, no estamos usando nombres de funciones y variables. Estamos probando la misma forma en que un usuario usaría nuestra aplicación y es a través del texto que verá.</p><p>Esperemos que esto le dé una buena idea de cómo probar con el <code>react-testing-library</code> y el principio rector, por lo general desea utilizar <code>getByText</code> la mayor parte del tiempo Hay algunas excepciones que veremos a medida que avancemos.</p><h3 id="usereducer"><strong>useReducer</strong></h3><p>Ahora podemos probar un componente con el gancho useReducer. Por supuesto, necesitaremos acciones y reductores para trabajar con nuestro componente, así que configurémoslos así:</p><p>Nuestra reductor</p><pre><code class="language-javascript">import * as ACTIONS from './actions'

export const initialState = {
    stateprop1: false,
}

export const Reducer1 = (state = initialState, action) =&gt; {
  switch(action.type) {
    case "SUCCESS":
      return {
        ...state,
        stateprop1: true,
      }
    case "FAILURE":
      return {
        ...state,
        stateprop1: false,
      }
    default:
      return state
  }
}</code></pre><p>Y las acciones:</p><pre><code>


export const SUCCESS = {
  type: 'SUCCESS'
}

export const FAILURE = {
  type: 'FAILURE'
}

</code></pre><p>mantendremos las cosas simples y usaremos acciones en lugar de creadores de acciones.</p><p>Y finalmente el componente que utilizará estas acciones y reductores:</p><pre><code class="language-javascript">import React, { useReducer } from 'react';
import * as ACTIONS from '../store/actions'
import * as Reducer from '../store/reducer'


const TestHookReducer = () =&gt; {
  const [reducerState, dispatch] = useReducer(Reducer.Reducer1, Reducer.initialState)

  const dispatchActionSuccess = () =&gt; {
    dispatch(ACTIONS.SUCCESS)
  }

  const dispatchActionFailure = () =&gt; {
    dispatch(ACTIONS.FAILURE)
  }


  return (
    &lt;div&gt;
       &lt;div&gt;
        {reducerState.stateprop1
           ? &lt;p&gt;stateprop1 is true&lt;/p&gt;
           : &lt;p&gt;stateprop1 is false&lt;/p&gt;}
       &lt;/div&gt;
       &lt;button onClick={dispatchActionSuccess}&gt;
         Dispatch Success
       &lt;/button&gt;
    &lt;/div&gt;
  )
}


export default TestHookReducer;
</code></pre><p>Este es un componente simple que cambiará <code>stateprop1</code> De falsa a verdadera mediante el envío de una accio <code>SUCCESS</code>.</p><p>Y ahora nuestra prueba.</p><pre><code class="language-javascript">import React from 'react';
import ReactDOM from 'react-dom';
import TestHookReducer from '../test_hook_reducer.js';
import {render, fireEvent, cleanup} from '@testing-library/react';
import * as Reducer from '../../store/reducer';
import * as ACTIONS from '../../store/actions';


afterEach(cleanup)

describe('test the reducer and actions', () =&gt; {
  it('should return the initial state', () =&gt; {
    expect(Reducer.initialState).toEqual({ stateprop1: false })
  })

  it('should change stateprop1 from false to true', () =&gt; {
    expect(Reducer.Reducer1(Reducer.initialState, ACTIONS.SUCCESS ))
      .toEqual({ stateprop1: true  })
  })
})

it('Reducer changes stateprop1 from false to true', () =&gt; {
   const { container, getByText } = render(&lt;TestHookReducer /&gt;);

   expect(getByText(/stateprop1 is/i).textContent).toBe("stateprop1 is false")

   fireEvent.click(getByText("Dispatch Success"))

   expect(getByText(/stateprop1 is/i).textContent).toBe("stateprop1 is true")
})</code></pre><p>Primero comenzamos probando nuestro reductor. Y podemos envolver las pruebas para el reductor en el bloque <code>describe</code>. Estas son pruebas bastante básicas que estamos usando para asegurarnos de que <strong><strong>initial state</strong></strong> es lo que queremos y las acciones producen el resultado que queremos.</p><p>Puede argumentar que probar el reductor es probar los detalles de implementación, pero en la práctica descubrí que probar acciones y reductores es una prueba unitaria que siempre es necesaria.</p><p>Este es un ejemplo simple, por lo que no parece ser un gran problema, pero en aplicaciones más grandes y complejas, no probar los reductores y las acciones puede resultar desastroso. Por lo tanto, las acciones y los reductores serían una excepción a la regla de detalles de implementación de prueba.</p><p>A continuación tenemos nuestras pruebas para el componente real. Observe nuevamente que aquí no estamos probando los detalles de implementación. Usamos el mismo patrón del ejemplo anterior de useState. Obtenemos nuestros nodos DOM por el texto y también encontramos y hacemos clic en el botón con el texto.</p><h3 id="usecontext"><strong>useContext</strong></h3><p>Ahora avancemos y probemos si un componente secundario puede actualizar el estado del contexto en un componente principal. Esto puede parecer complejo, pero es bastante simple y directo.</p><p>Primero necesitaremos nuestro objeto de contexto que podemos inicializar en su propio archivo.</p><pre><code class="language-javascript">import React from 'react';

const Context = React.createContext()

export default Context
</code></pre><p>También necesitamos nuestro componente de aplicación principal que contendrá el proveedor de contexto. El valor transmitido al <code>Provider</code> será el valor del estado y de la función <code>setState</code> &nbsp;del componente <code>App.js</code>.</p><pre><code class="language-javascript">import React, { useState } from 'react';
import TestHookContext from './components/react-testing-lib/test_hook_context';


import Context from './components/store/context';


const App = () =&gt; {
  const [state, setState] = useState("Some Text")
  

  const changeText = () =&gt; {
    setState("Some Other Text")
  }


  return (
    &lt;div className="App"&gt;
    &lt;h1&gt; Basic Hook useContext&lt;/h1&gt;
     &lt;Context.Provider value={{changeTextProp: changeText,
                               stateProp: state
                                 }} &gt;
        &lt;TestHookContext /&gt;
     &lt;/Context.Provider&gt;
    &lt;/div&gt;
  );
}

export default App;</code></pre><p>Y para nuestro componente</p><pre><code class="language-javascript">import React, { useContext } from 'react';

import Context from '../store/context';

const TestHookContext = () =&gt; {
  const context = useContext(Context)

  return (
    &lt;div&gt;
    &lt;button onClick={context.changeTextProp}&gt;
        Change Text
    &lt;/button&gt;
      &lt;p&gt;{context.stateProp}&lt;/p&gt;
    &lt;/div&gt;
  )
}


export default TestHookContext;</code></pre><p>Tenemos un componente simple que muestra el texto que inicializamos en <code>App.js</code> Y también pasamos la funcion <code>setState</code> del metodo <code>onClick</code>.</p><p><strong><strong>Not</strong>a<strong>:</strong></strong> El estado es cambiado, inicializado y contenido en nuestro componente <code>App.js</code>.Simplemente hemos pasado el valor del estado y la funciób <code>setState</code> a nuestro componente secundario a través del contexto, pero en última instancia, el estado se maneja en el componente <code>App.js</code>. Esto será importante para entender nuestra prueba.</p><p>Y nuestra prueba:</p><pre><code class="language-javascript">import React from 'react';
import ReactDOM from 'react-dom';
import TestHookContext from '../test_hook_context.js';
import {act, render, fireEvent, cleanup} from '@testing-library/react';
import App from '../../../App'

import Context from '../../store/context';

afterEach(cleanup)

it('Context value is updated by child component', () =&gt; {

   const { container, getByText } = render(&lt;App&gt;
                                            &lt;Context.Provider&gt;
                                             &lt;TestHookContext /&gt;
                                            &lt;/Context.Provider&gt;
                                           &lt;/App&gt;);

   expect(getByText(/Some/i).textContent).toBe("Some Text")

   fireEvent.click(getByText("Change Text"))

   expect(getByText(/Some/i).textContent).toBe("Some Other Text")
})
</code></pre><p>Incluso para el contexto, puedes ver que no rompemos nuestro patrón de pruebas, aún encontramos y simulamos nuestros eventos con el texto.</p><p>He incluido los componentes <code>&lt;Context.Provider/&gt;</code> y <code>&lt;TestHookContext /&gt; </code> en la función de representación porque hace que el código sea más fácil de leer, pero en realidad no necesitamos ninguno de ellos. Nuestra prueba seguirá funcionando si aprobamos solo el componente <code>&lt;App /&gt;</code> a la función de renderizado.</p><pre><code>const { container, getByText } = render(&lt;App/&gt;) </code></pre><p>¿Por qué es este el caso?</p><p>Pensemos en lo que sabemos sobre el contexto. Todo el estado del contexto se maneja en <code>App.js</code>, por esta razón, este es el componente principal que estamos probando, aunque parece que estamos probando el componente secundario que usa el Hook <strong><strong>useContext</strong></strong>. Este código también funciona debido a <strong><strong>mount/render</strong></strong>. Como sabemos, en el renderizado superficial, los componentes secundarios son <strong><strong>not rendered</strong></strong>, pero son en mount/render. Ya que <code>&lt;Context.Provider /&gt;</code> y <code>&lt;TestHookContext /&gt;</code> ambos son componentes secundarios de <code>&lt;App /&gt;</code> se procesan automáticamente.</p><h3 id="formularios-de-componentes-controlados">Formularios de componentes controlados</h3><p>Un formulario de componente controlado esencialmente significa que el formulario funcionará a través del estado React en lugar de que el formulario mantenga su propio estado. Lo que significa que el controlador <code>onChange</code> guardará el texto de entrada en el estado React en cada pulsación de tecla.</p><p>Probar el formulario será un poco diferente de lo que hemos visto hasta ahora, pero intentaremos mantener nuestro principio rector en mente.</p><pre><code class="language-javascript">import React, { useState } from 'react';

const HooksForm1 = () =&gt; {
  const [valueChange, setValueChange] = useState('')
  const [valueSubmit, setValueSubmit] = useState('')

  const handleChange = (event) =&gt; (
    setValueChange(event.target.value)
  );

  const handleSubmit = (event) =&gt; {
    event.preventDefault();
    setValueSubmit(event.target.text1.value)
  };

    return (
      &lt;div&gt;
       &lt;h1&gt; React Hooks Form &lt;/h1&gt;
        &lt;form data-testid="form" onSubmit={handleSubmit}&gt;
          &lt;label htmlFor="text1"&gt;Input Text:&lt;/label&gt;
          &lt;input id="text1" onChange={handleChange} type="text" /&gt;
          &lt;button type="submit"&gt;Submit&lt;/button&gt;
        &lt;/form&gt;
        &lt;h3&gt;React State:&lt;/h3&gt;
          &lt;p&gt;Change: {valueChange}&lt;/p&gt;
          &lt;p&gt;Submit Value: {valueSubmit}&lt;/p&gt;
        &lt;br /&gt;
      &lt;/div&gt;
    )
}


export default HooksForm1;</code></pre><p>Este es un formulario básico que tenemos aquí y también mostramos el valor del cambio y el valor de envío en nuestro JSX. Tenemos el atributo <code>data-testid="form"</code> &nbsp;que usaremos en nuestra prueba para la consulta del formulario.</p><p>Y nuestras pruebas:</p><pre><code class="language-javascript">import React from 'react';
import ReactDOM from 'react-dom';
import HooksForm1 from '../test_hook_form.js';
import {render, fireEvent, cleanup} from '@testing-library/react';

afterEach(cleanup)

//testing a controlled component form.
it('Inputing text updates the state', () =&gt; {
    const { getByText, getByLabelText } = render(&lt;HooksForm1 /&gt;);

    expect(getByText(/Change/i).textContent).toBe("Change: ")

    fireEvent.change(getByLabelText("Input Text:"), {target: {value: 'Text' } } )

    expect(getByText(/Change/i).textContent).not.toBe("Change: ")
 })


 it('submiting a form works correctly', () =&gt; {
     const { getByTestId, getByText } = render(&lt;HooksForm1 /&gt;);

     expect(getByText(/Submit Value/i).textContent).toBe("Submit Value: ")

     fireEvent.submit(getByTestId("form"), {target: {text1: {value: 'Text' } } })

     expect(getByText(/Submit Value/i).textContent).not.toBe("Submit Value: ")
  })</code></pre><p>Dado que un elemento de entrada vacío no tiene texto, usaremos una función <code>getByLabelText()</code> para obtener el nodo de entrada. Esto aún se mantendrá con nuestro principio rector, ya que el texto de la etiqueta es lo que el usuario leerá antes de ingresar el texto.</p><p>Ten en cuenta que activaremos el evento <code>.change()</code> en lugar del evento habitual <code>.click()</code> También pasamos datos ficticios en forma de:</p><p><code>{ target: { value: "Text" } }</code></p><p>Dado que se accederá al valor del formulario en forma de <code>event.target.value</code>,esto es lo que le pasamos al evento simulado.</p><p>Dado que generalmente no sabremos cuál es el texto que enviará el usuario, podemos usar una palabra clave <code>.not</code> para asegurarnos de que el texto haya cambiado en nuestro método de representación.</p><p>Podemos probar el envío del formulario de manera similar. La única diferencia es que usamos el evento <code>.submit()</code> y pasamos datos ficticios de esta manera:</p><p><code>{ target: { text1: { value: 'Text' } } }</code></p><p>Así es como se accede a los datos del formulario desde el evento sintético cuando un usuario envía un formulario. donde <code>text1</code> &nbsp;es el id de nuestro elemento de entrada. Tendremos que romper nuestro patrón un poco aquí y usar el atributo <code>data-testid="form"</code> para consultar el formulario, ya que realmente no hay otra forma de obtener el formulario.</p><p>Y eso es todo por la forma. No es tan diferente de nuestros otros ejemplos. Si crees que lo entendiste, pasemos a algo un poco más complejo.</p><h3 id="useeffect-y-solicitudes-de-api-con-axios">useEffect y solicitudes de API con axios</h3><p>Ahora veamos cómo probaríamos el enlace <strong><strong>useEffect hook</strong></strong> y las solicitudes de API. sto será bastante diferente a lo que hemos visto hasta ahora.</p><p>Digamos que tenemos una URL pasada a un componente secundario desde el padre raíz.</p><pre><code class="language-javascript">
...

     &lt;TestAxios url='https://jsonplaceholder.typicode.com/posts/1' /&gt;
     
 ... </code></pre><p>Y el componente en sí.</p><pre><code class="language-javascript">import React, { useState, useEffect } from 'react';
import axios from 'axios';


const TestAxios = (props) =&gt; {
  const [state, setState] = useState()

  useEffect(() =&gt; {
    axios.get(props.url)
      .then(res =&gt; setState(res.data))
  }, [])


  return (
    &lt;div&gt;
    &lt;h1&gt; Axios Test &lt;/h1&gt;
        {state
          ? &lt;p data-testid="title"&gt;{state.title}&lt;/p&gt;
          : &lt;p&gt;...Loading&lt;/p&gt;}
    &lt;/div&gt;
  )
}


export default TestAxios;</code></pre><p>Simplemente hacemos una solicitud de API y guardamos los resultados en el estado local. También usamos una expresión ternaria en nuestro método de representación para esperar hasta que se complete la solicitud para mostrar los datos del título del marcador de posición json.</p><p>Notarás que nuevamente tendremos que hacer uso del atributo <code>data-testid</code> por necesidad, y nuevamente es un detalle de implementación ya que un usuario no verá ni interactuará con este atributo de ninguna manera, pero esto es más realista, ya que generalmente no conocerá el texto de una solicitud API de antemano.</p><p>También usaremos simulacros en esta prueba.</p><p>Un <strong><strong>mock</strong></strong> es una forma de simular un comportamiento que en realidad no queremos hacer en nuestras pruebas. Por ejemplo, nos burlamos de las solicitudes de API porque no queremos realizar solicitudes reales en nuestras pruebas.</p><p>No queremos realizar solicitudes de API reales en nuestras pruebas por varias razones: hará que nuestras pruebas sean mucho más lentas, podría darnos un falso negativo, la solicitud de API nos costará dinero o estropearemos nuestra base de datos con datos de prueba.</p><pre><code>import React from 'react';
import ReactDOM from 'react-dom';
import TestAxios from '../test_axios.js';
import {act, render, fireEvent, cleanup, waitForElement} from '@testing-library/react';

import axiosMock from "axios";

</code></pre><p>Tenemos nuestras importaciones habituales pero notarás algo peculiar. Estamos importando <code>axiosMock</code> de la biblioteca <code>axios</code>. No estamos importando un objeto <code>axios</code> simulado de la biblioteca. En realidad, nos estamos burlando de la propia biblioteca de <code>axios</code>.</p><p>¿Cómo?</p><p>Mediante el uso de la funcionalidad de burla que ofrece jest.</p><p>Primero crearemos una <code>__mocks__</code> adyacente a nuestra carpeta de prueba, algo como esto.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/07/image-13.png" class="kg-image" alt="image-13" width="600" height="400" loading="lazy"></figure><p>Y dentro de la carpeta de simulacros tenemos un archivo <code>axios.js</code> y esta es nuestra biblioteca <strong><strong>axios </strong></strong>falsa. Y dentro de nuestra biblioteca de <strong>axios</strong> falsos tenemos nuestra función simulacro de broma.</p><p>Las funciones simuladas nos permiten usar funciones en nuestro entorno de broma sin tener que implementar la lógica real de la función.</p><p>Básicamente, no vamos a implementar la lógica real detrás de una solicitud de obtención de axios. Solo usaremos esta función simulada en su lugar.</p><pre><code class="language-javascript">export default {
  get: jest.fn(() =&gt; Promise.resolve({ data: {} }) )
};
</code></pre><p>Aquí tenemos nuestra función de obtención falsa. Es una función simple que en realidad es un objeto JS. <code>get</code> es nuestra clave y el valor es la <strong><strong>mock function</strong></strong>. Como una solicitud de API de <strong><strong>axios</strong></strong>, resolvemos una promesa. No pasaremos ningún dato aquí, lo haremos en nuestra configuración de prueba.</p><p>Ahora nuestra configuración de prueba</p><pre><code class="language-javascript">//imports
...

afterEach(cleanup)

it('Async axios request works', async () =&gt; {
  axiosMock.get.mockResolvedValue({data: { title: 'some title' } })

  const url = 'https://jsonplaceholder.typicode.com/posts/1'
  const { getByText, getByTestId, rerender } = render(&lt;TestAxios url={url} /&gt;);

  expect(getByText(/...Loading/i).textContent).toBe("...Loading")

  const resolvedEl = await waitForElement(() =&gt; getByTestId("title"));

  expect((resolvedEl).textContent).toBe("some title")

  expect(axiosMock.get).toHaveBeenCalledTimes(1);
  expect(axiosMock.get).toHaveBeenCalledWith(url);
 })</code></pre><p>Lo primero que hacemos en nuestra prueba es llamar a nuestra falsa <strong><strong>axios get request</strong>,</strong> y simular el valor resuelto con la función <code>mockResolvedValue</code> que ofrece jest. Esta función hace exactamente lo que dice su nombre, resuelve una promesa con los datos que le pasamos, lo que simula lo que hace axios.</p><p>Esta función debe llamarse antes de nuestra función <code>render()</code>, de lo contrario, la prueba no funcionará. Porque recuerda que nos estamos burlando de la propia <strong>biblioteca de axios</strong>. Cuando nuestro componente ejecuta <strong>la importación axios desde 'axios'</strong>; comando <code>import axios from 'axios';</code> falsa en lugar de la real y esta axios falsa se sustituirá en nuestro componente donde sea que usemos axios.</p><p>A continuación, obtenemos nuestro nodo de texto "...Loading", ya que esto es lo que se mostrará antes de que se resuelva la promesa. Después de esto, tenemos una función que no hemos visto antes, la función <code>waitForElement()</code>, que esperará hasta que se resuelva la promesa antes de pasar a la siguiente afirmación.</p><p>Observa también las palabras clave <strong>await</strong> y <strong>async</strong>, que se usan exactamente de la misma manera que se usan en un entorno que no es de prueba.</p><p>Una vez resuelto, el nodo DOM tendrá el texto de "algún título", que son los datos que pasamos a nuestra biblioteca falsa de axios.</p><p>A continuación, nos aseguramos de que la solicitud solo se haya llamado una vez y con la URL correcta. Aunque estamos probando la URL, no hicimos una solicitud de API con esta URL.</p><p>Y esto es todo para las solicitudes de API con axios. En la siguiente sección veremos las pruebas e a e con ciprés.</p><h2 id="cypress"><strong>Cypress</strong></h2><p>Ahora repasemos Cypress, que creo que es el mejor marco para ejecutar pruebas de e a e. Ahora estamos más tiempo en la tierra de las bromas, ahora trabajaremos únicamente con cypress, que tiene su propio entorno de prueba y sintaxis.</p><p>Cypress es bastante sorprendente y poderoso. De hecho, es tan asombroso y poderoso que podemos ejecutar todas las pruebas que acabamos de revisar en un bloque de prueba y ver cómo Cypress ejecuta estas pruebas en tiempo real en un navegador simulado.</p><p>Muy bien, ¿eh?</p><p>Creo que sí. De todos modos, antes de que podamos hacer eso, necesitamos configurar Cypress. Sorprendentemente, Cypress se puede instalar como un módulo npm normal.</p><p><code>npm install cypress</code></p><p>Para ejecutar cypress necesitará usar este comando.</p><p><code>node_modules/.bin/cypress open</code></p><p>Si eso parece engorroso de escribir cada vez que desee abrir cypress, puede agregarlo a su paquete.json.</p><pre><code class="language-javascript">...

  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "cypress": "node_modules/.bin/cypress open", 
    
   ...</code></pre><p>Esto le permitirá abrir cypress con solo el comando <code>npm run cypress</code>.</p><p>Abrir cypress te dará una GUI que se ve así.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/07/image-14.png" class="kg-image" alt="image-14" width="600" height="400" loading="lazy"></figure><p>Para ejecutar realmente las pruebas de Cypress, su aplicación deberá estar ejecutándose al mismo tiempo, lo que veremos en un segundo.</p><p>Ejecutar el comando <code>cypress open</code> le dará una configuración básica de cypress y creará algunos archivos y carpetas automáticamente. Se creará una carpeta Cypress en la raíz del proyecto. Escribiremos nuestro código en la carpeta de integración.</p><p>Podemos comenzar eliminando la carpeta de ejemplos. A diferencia de jest, los archivos de ciprés tienen una extensión <code>.spec.js</code>. Debido a que esta es una prueba de e a e, la ejecutaremos en nuestro archivo &nbsp;<code>App.js</code> principal. Entonces debería tener una estructura de directorios que ahora se ve así.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/07/image-21.png" class="kg-image" alt="image-21" width="600" height="400" loading="lazy"></figure><p>También podemos establecer una URL base en el archivo cypress.json. Así como esto:</p><p><code>{ "baseUrl": "<a href="http://localhost:3000/">http://localhost:3000</a>" }</code></p><p>Ahora para nuestra gran prueba monolítica</p><pre><code class="language-javascript">import React from 'react';

describe ('complete e to e test', () =&gt; {
  it('e to e test', () =&gt; {
    cy.visit('/')
    //counter test
    cy.contains("Clicked: 0")
      .click()
    cy.contains("Clicked: 1")
    // basic hooks test
    cy.contains("Initial State")
    cy.contains("State Change Button")
      .click()
    cy.contains("Initial State Changed")
    cy.contains("Moe")
    cy.contains("Change Name")
      .click()
    cy.contains("Steve")
    //useReducer test
    cy.contains('stateprop1 is false')
    cy.contains('Dispatch Success')
      .click()
    cy.contains('stateprop1 is true')
    //useContext test
    cy.contains("Some Text")
    cy.contains('Change Text')
      .click()
    cy.contains("Some Other Text")
    //form test
    cy.get('#text1')
      .type('New Text {enter}')
    cy.contains("Change: New Text")
    cy.contains("Submit Value: New Text")
    //axios test
    cy.request('https://jsonplaceholder.typicode.com/posts/1')
      .should(res =&gt; {
          expect(res.body).not.to.be.null
          cy.contains(res.body.title)
        })
  });
});</code></pre><p>Como se mencionó, estamos ejecutando todas las pruebas que acabamos de revisar en un bloque de prueba. He separado cada sección con un comentario para que sea más fácil de ver.</p><p>Nuestra prueba puede parecer intimidante al principio, pero la mayoría de las pruebas individuales seguirán un patrón básico de arrange-act-assert pattern.</p><pre><code class="language-javascript">
cy.contains(Some innerHTML text of DOM node)

cy.contains (text of button)
.click()

cy.contains(Updated innerHTML text of DOM node)
</code></pre><p>Dado que esta es una prueba de e a e, no encontrará ninguna burla. Nuestra aplicación se ejecutará en su versión de desarrollo completo en un navegador simulado con una interfaz de usuario. Esto será lo más cerca que podamos de probar nuestra aplicación de una manera realista.</p><p>A diferencia de las pruebas unitarias y de integración, no necesitamos afirmar explícitamente algunas cosas. Esto se debe a que algunos comandos de Cypress tienen afirmaciones predeterminadas integradas. Las <strong>aserciones predeterminadas</strong> son exactamente como suenan, se afirman de forma predeterminada, por lo que no es necesario agregar un comparador.</p><p>Aserciones predeterminadas de Cypress</p><p>Los comandos están encadenados, por lo que el orden es importante y un comando esperará hasta que se complete un comando anterior antes de ejecutarse.</p><p>Incluso al realizar pruebas con Cypress, nos apegaremos a nuestra filosofía de no probar los detalles de implementación. En la práctica, esto significará que no usaremos clases, ids o propiedades html/css como selectores si podemos evitarlo. La única vez que necesitaremos usar id es para obtener nuestro elemento de entrada de formulario.</p><p>Haremos uso del comando <code>cy.contains()</code> que devolverá un nodo DOM con texto coincidente. Ver e interactuar con el texto en la interfaz de usuario es lo que hará nuestro usuario final, por lo que probar de esta manera estará en línea con nuestro principio rector.</p><p>Dado que no estamos criticando ni burlándonos de nada, notará que nuestras pruebas se verán muy simples. Esto es bueno ya que se trata de una aplicación que se ejecuta en vivo, nuestras pruebas no tendrán valores artificiales.</p><p>En nuestra prueba de axios, haremos una solicitud http real a nuestro punto final. Hacer una solicitud http real en una prueba e to e es común. Luego comprobaremos si ese valor no es nulo. Luego, asegúrese de que los datos de la respuesta aparezcan en nuestra interfaz de usuario.</p><p>Si se hace correctamente, debería ver que Cypress ejecutó con éxito las pruebas en cromo.<br></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/07/image-22.png" class="kg-image" alt="image-22" width="600" height="400" loading="lazy"></figure><h2 id="integraci-n-continua">Integración continua</h2><p>Hacer un seguimiento y ejecutar todas estas pruebas manualmente puede volverse tedioso. Así que tenemos Integración Continua, una forma de ejecutar automáticamente nuestras pruebas de forma continua.</p><h3 id="travis-ci"><strong>Travis CI</strong></h3><p>Para simplificar las cosas, solo usaremos Travis CI para nuestra integración continua. Sin embargo, debe saber que hay configuraciones de CI mucho más complejas que usan Docker y Jenkins.</p><p>Deberá registrarse para obtener una cuenta de Travis y Github, ambas son afortunadamente gratuitas.</p><p>Sugeriría simplemente usar la opción "Registrarse con Github" en Travis CI.</p><p>Una vez allí, puede ir al icono de su perfil y hacer clic en el botón deslizante junto al repositorio en el que desea CI.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/07/image-15.png" class="kg-image" alt="image-15" width="600" height="400" loading="lazy"></figure><p>Para que Travis CI sepa qué hacer, necesitaremos configurar un archivo .travis.yml en la raíz de nuestro proyecto.</p><pre><code class="language-yaml">language: node_js

node_js: 
  - stable
  
  
install:
  - npm install

script:
  - npm run test
  - npm run coveralls</code></pre><p>Básicamente, esto le dice a Travis que estamos usando node_js, descargamos la versión estable, instalamos las dependencias y ejecutamos el comando npm run test y npm run coveralls.</p><p>Y esto es todo. Puede ir al panel de control e iniciar la compilación. Travis ejecutará las pruebas automáticamente y le dará un resultado como este. Si sus pruebas pasan, está listo para comenzar. Si fallan, su compilación fallará y deberá corregir su código y reiniciar la compilación.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/07/image-16.png" class="kg-image" alt="image-16" width="600" height="400" loading="lazy"></figure><h3 id="coveralls"><strong>Coveralls</strong></h3><p>Coverall nos brinda un informe de cobertura que esencialmente nos dice cuánto de nuestro código se está probando.</p><p>Deberás registrarte en overoles y sincronizar con tu cuenta de github. Similar a Travis CI, simplemente vaya a la pestaña Agregar repositorios y active el repositorio que también activó en Travis CI.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/07/image-17.png" class="kg-image" alt="image-17" width="600" height="400" loading="lazy"></figure><p>A continuación, ve a tu archivo package.json y agrega esta línea de código</p><pre><code>  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --coverage",
    "eject": "react-scripts eject",
    "cypress": "node_modules/.bin/cypress open", 
    "coveralls": "cat ./coverage/lcov.info | node node_modules/.bin/coveralls"
  },</code></pre><p>Asegúrete de agregar el indicador <code>--coverage</code> al comando de prueba de <code>react-scripts test</code>. Esto es lo que generará los datos de cobertura que usará overoles para generar un informe de cobertura.</p><p>Y puedes ver estos datos de cobertura en la consola de Travis CI después de que se hayan ejecutado las pruebas.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/07/image-18.png" class="kg-image" alt="image-18" width="600" height="400" loading="lazy"></figure><p>Dado que no estamos tratando con un repositorio privado o Travis CI pro, no tenemos que preocuparnos por los tokens de repositorio.</p><p>Una vez que hayas terminado, puedes agregar una insignia a su repositorio README copiando el enlace provisto en el tablero.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/07/image-19.png" class="kg-image" alt="image-19" width="600" height="400" loading="lazy"></figure><p>Se verá así.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2019/07/image-20.png" class="kg-image" alt="image-20" width="600" height="400" loading="lazy"></figure><h2 id="conclusion"><strong>Conclusion</strong></h2><p>Cuéntate entre el 20% de los mejores desarrolladores en términos de habilidad de prueba de React si completó todo el tutorial.</p><p>Gracias por leer. </p><blockquote>Puedes seguirme en twitter para más tutoriales en el futuro: <a href="https://twitter.com/iqbal125sf?lang=en">https://twitter.com/iqbal125sf?lang=en</a></blockquote> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo usar la propiedad de posición en CSS para alinear elementos ]]>
                </title>
                <description>
                    <![CDATA[ Posicionar elementos con CSS en el desarrollo web no es tan fácil como parece. Las cosas pueden complicarse rápidamente a medida que su proyecto crece y sin tener una buena comprensión de cómo CSS se ocupa de la alineación de elementos HTML, no podrá solucionar sus problemas de alineación. Hay ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-usar-la-propiedad-posicion-en-css-para-alinear-elementos/</link>
                <guid isPermaLink="false">62fe62097b4ec209a40242e4</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jorge Sierra ]]>
                </dc:creator>
                <pubDate>Thu, 22 Sep 2022 01:15:25 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2022/09/1_fXBo56b0tanSCNHo4O2eWw.jpeg" 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-use-the-position-property-in-css-to-align-elements-d8f49c403a26/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to use the position property in CSS to align elements</a>
      </p><p>Posicionar elementos con CSS en el desarrollo web no es tan fácil como parece. Las cosas pueden complicarse rápidamente a medida que su proyecto crece y sin tener una buena comprensión de cómo CSS se ocupa de la alineación de elementos HTML, no podrá solucionar sus problemas de alineación.</p><p>Hay diferentes maneras/métodos para posicionar elementos con CSS puro. Usando las propiedades CSS <strong><strong>float, display </strong> </strong>y <strong><strong>position </strong></strong>son los métodos más comunes.</p><p>En este artículo, explicaré una de las formas más confusas de alinear elementos con CSS puro: la <strong>propiedad de position</strong>.</p><p>Vamos a empezar...</p><h3 id="css-position-y-propiedades-auxiliares"><strong>CSS Position </strong>y propiedades auxiliares</h3><p>Así que hay 5 valores principales de la<strong><strong><strong><strong> </strong></strong>Propiedad <strong><strong>Position:</strong></strong></strong></strong></p><p><code>position: static | relative | absolute | fixed | sticky</code></p><p>y propiedades adicionales para establecer las coordenadas de un elemento (Las llamo <strong>“propiedades auxiliares”</strong>):</p><p><code>top | right | bottom | left</code> y la <code>z-index</code></p><blockquote><em><em><strong>Nota importante</strong><em><em><strong><strong>:</strong></strong> </em></em></em></em>Las propiedades auxiliares no funcionan sin una posición declarada o con<em><em><em><em> <strong><strong><strong><strong>position: static.</strong></strong></strong></strong></em></em></em></em></blockquote><h4 id="-qu-es-este-z-index"><strong>¿Qué es este z-index?</strong></h4><p>Nosotros tenemos altura y anchura (x, y) son 2 dimensiones. Z es la 3.ª dimensión. Un elemento en la página web aparece delante de otros elementos como su <code>z-index</code> aumenta el valor.</p><blockquote><strong><strong><strong><strong>Z-index</strong></strong></strong></strong> no funciona con <code>position: static</code> o sin una posición declarada.</blockquote><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*Dc7075K8xmYZQAqn6BaJPg.png" class="kg-image" alt="1*Dc7075K8xmYZQAqn6BaJPg" width="485" height="203" loading="lazy"><figcaption>Elements are ordered from back to front, as their <strong class="markup--strong markup--figure-strong" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 700; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 17.6px; vertical-align: baseline; color: var(--gray85);">z-index&nbsp;</strong>increase</figcaption></figure><p>Ahora sigamos con los valores de las propiedades de <strong><strong><strong><strong>Position</strong></strong></strong></strong>...</p><h3 id="1-static"><strong>1. Static</strong></h3><p><code>position: static</code> es el <strong>valor por defecto</strong>. Ya sea que lo declaremos o no, los elementos se colocan en un orden normal en la página web. Pongamos un ejemplo:</p><p>Primero, definimos nuestra estructura HTML:</p><pre><code class="language-html">&lt;body&gt;
  &lt;div class="box-orange"&gt;&lt;/div&gt;
  &lt;div class="box-blue"&gt;&lt;/div&gt;
&lt;/body&gt;</code></pre><p>Luego, creamos 2 cajas y definimos sus width, height y position:</p><pre><code class="language-css">.box-orange {          // sin ninguna declaracion position
  background: orange;
  height: 100px;
  width: 100px;       
}

.box-blue {
  background: lightskyblue;
  height: 100px;
  width: 100px; 
  position: static;   // Declarado como estatico
}</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*atA27-7dzI4wKkg_HfAtLw.png" class="kg-image" alt="1*atA27-7dzI4wKkg_HfAtLw" width="346" height="219" loading="lazy"><figcaption>same result with &amp; without<strong class="markup--strong markup--figure-strong" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 700; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 17.6px; vertical-align: baseline; color: var(--gray85);"> position: static</strong></figcaption></figure><p>Como podemos ver en la imagen, definir la <strong>position</strong>: <strong>static </strong>o no, no hace ninguna diferencia. Las cajas se colocan de acuerdo con el <strong>flujo normal de documento</strong>.</p><h3 id="2-relative"><strong>2. Relative</strong></h3><p><code>position: relative</code>: <strong>La nueva posición de un elemento en relación con su posición normal.</strong></p><p>Empezando con <code>position: relative</code> y para todos valor de position <strong><strong><strong><strong>non-static</strong></strong></strong></strong>, podemos cambiar la posición predeterminada de un elemento usando las propiedades auxiliares que mencioné anteriormente.</p><p>Movamos el cuadro naranja al lado del azul.</p><pre><code class="language-css">.box-orange {
  position: relative;  // Ahora estamos listos para mover el elemento
  background: orange;
  width: 100px;
  height: 100px;
  top: 100px;         // 100px para top en relacion con su position antigua
  left: 100px;        // 100px para left
}</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*noqTpZ-EBTftlKdFi48Iiw.png" class="kg-image" alt="1*noqTpZ-EBTftlKdFi48Iiw" width="385" height="215" loading="lazy"><figcaption><strong class="markup--strong markup--figure-strong" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 700; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 17.6px; vertical-align: baseline; color: var(--gray85);">Orange box is moved 100px to bottom &amp; right, relative to its normal&nbsp;position</strong></figcaption></figure><blockquote><em><em><em><em>NOT</em></em>A<em><em>: U</em></em>sando<em><em> <strong><strong><strong><strong>position: relative</strong></strong></strong></strong> </em></em>por un elemento<em><em>, </em></em></em></em>no afecta las posiciones de otros elementos.</blockquote><h3 id="3-absolute"><strong>3. Absolute</strong></h3><p>En <code>position: relative</code>, el elemento está posicionado <strong>Relativo a sí mismo<strong><strong><strong>.</strong></strong> </strong></strong>Sin embargo, un elemento <strong>absolutamente</strong> posicionado es <strong>relativo a su padre.</strong></p><p>Un elemento con <code>position: absolute</code> se elimina del flujo normal de documentos. Se posiciona automáticamente en el punto de inicio (<strong><strong><strong><strong>top-left </strong></strong></strong>esquina<strong><strong><strong>)</strong></strong></strong></strong> de su elemento padre. Si no tiene ningún elemento principal, Entonces el siguiente<strong><strong><strong><strong> </strong></strong>Elemento<strong><strong> &lt;html&gt;</strong></strong></strong></strong> Será su padre.</p><p>Dado que <code>position: absolute</code> elimina el elemento del flujo del documento, otros elementos se ven afectados y se comportan como si el elemento se eliminara por completo de la página web.</p><p>Agreguemos un <strong>container</strong> como elemento principal:</p><pre><code class="language-html">&lt;body&gt;
  &lt;div class="container"&gt;
    &lt;div class="box-orange"&gt;&lt;/div&gt;
    &lt;div class="box-blue"&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/body&gt;</code></pre><pre><code class="language-css">.box-orange {
  position: absolute;
  background: orange;
  width: 100px;
  height: 100px;
}</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*C15mDxOdtFLkXLcFaVRYBQ.png" class="kg-image" alt="1*C15mDxOdtFLkXLcFaVRYBQ" width="515" height="193" loading="lazy"><figcaption><strong class="markup--strong markup--figure-strong" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 700; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 17.6px; vertical-align: baseline; color: var(--gray85);">position: absolute</strong> takes the element to the <strong class="markup--strong markup--figure-strong" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 700; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 17.6px; vertical-align: baseline; color: var(--gray85);">beginning</strong> of its&nbsp;parent</figcaption></figure><p>Ahora parece que la caja azul ha desaparecido, pero no es así. El cuadro azul se comporta como si se quitara el cuadro naranja, por lo que sube al lugar del cuadro naranja.</p><p>Movamos el cuadro naranja 5 píxeles:</p><pre><code class="language-css">.box-orange {
  position: absolute;
  background: orange;
  width: 100px;
  height: 100px;
  left: 5px;
  top: 5px;
}</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*ss6uEQz9Bbdrdst8kNiHqQ.png" class="kg-image" alt="1*ss6uEQz9Bbdrdst8kNiHqQ" width="523" height="124" loading="lazy"><figcaption><strong class="markup--strong markup--figure-strong" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 700; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 17.6px; vertical-align: baseline; color: var(--gray85);">Now we can see the blue&nbsp;box</strong></figcaption></figure><p>Las coordenadas de un elemento con posición <strong>absoluta</strong> son relativas a su padre si el padre también tiene una <strong>non-static position.<strong><strong><strong> </strong></strong></strong></strong>De lo contrario, las propiedades auxiliares colocan el elemento en relación con el inicial.</p><pre><code class="language-css">.container {
  position: relative;
  background: lightgray;
}

.box-orange {
  position: absolute;
  background: orange;
  width: 100px;
  height: 100px;
  right: 5px;    // 5px relative to the most-right of parent
}</code></pre><figure class="kg-card kg-image-card"><img src="https://cdn-media-1.freecodecamp.org/images/1*AEX5fn8t0MJZCo4Lx52Uaw.png" class="kg-image" alt="1*AEX5fn8t0MJZCo4Lx52Uaw" width="480" height="130" loading="lazy"></figure><h3 id="4-fixed"><strong>4. Fixed</strong></h3><p>Me gusta <code>position: absolute</code>, los elementos de posición fija también se eliminan del flujo de documentos normal. Las diferencias son:</p><ul><li>Solo son <strong><strong>relativos al documento<strong><strong> &lt;html&gt;, </strong></strong></strong></strong>y no a ningún otro padre.</li><li><strong>N<strong>o</strong> se<strong> </strong>ven <strong>afectados por el desplazamiento</strong></strong>.</li></ul><pre><code class="language-css">.container {
  position: relative;
  background: lightgray;
}

.box-orange {
  position: fixed;
  background: orange;
  width: 100px;
  height: 100px;
  right: 5px;    // 5px en relacion con el mas derecho de los padres
}</code></pre><p>Aquí en el ejemplo, Cambio la posición del cuadro naranja a <strong><strong><strong><strong>fixed</strong></strong></strong></strong>, y esta vez es relativo 5px a la derecha del documento <strong><strong><strong><strong>&lt;html&gt;</strong></strong></strong></strong>, no su<strong><strong><strong><strong> p</strong></strong>adre<strong><strong> (container)</strong></strong>:</strong></strong></p><figure class="kg-card kg-embed-card"><iframe id="cp_embed_EebjaB" src="https://codepen.io/cem_eygi/embed/preview/EebjaB?height=300&amp;slug-hash=EebjaB&amp;default-tabs=css,result&amp;host=https://codepen.io" title="Embedded content" scrolling="no" frameborder="0" height="300" allowtransparency="true" class="cp_embed_iframe" loading="lazy" data-dashlane-frameid="554" 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-size: 22px; vertical-align: middle; width: 760px; overflow: hidden;"></iframe></figure><p>Como podemos ver, desplazarse por la página no afecta a la <strong><strong><strong><strong>fixed</strong></strong></strong></strong> caja posicionada. Ya no es relativa a su padre (contenedor).</p><h3 id="5-sticky"><strong>5. Sticky</strong></h3><p><code>position: sticky</code> puede explicarse como una mezcla de <code>position: relative</code> y <code>position: fixed</code>.</p><p>Se comporta hasta un punto declarado como <code>position: relative</code>, después de eso cambia su comportamiento a <code>position: fixed</code>. La mejor manera de explicar <strong><strong><strong><strong>position: sticky</strong></strong> </strong></strong>es con un ejemplo: </p><figure class="kg-card kg-embed-card"><iframe id="cp_embed_RYjrWz" src="https://codepen.io/cem_eygi/embed/preview/RYjrWz?height=300&amp;slug-hash=RYjrWz&amp;default-tabs=css,result&amp;host=https://codepen.io" title="Embedded content" scrolling="no" frameborder="0" height="300" allowtransparency="true" class="cp_embed_iframe" loading="lazy" data-dashlane-frameid="555" 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-size: 22px; vertical-align: middle; width: 760px; overflow: hidden;"></iframe></figure><p><strong><strong><strong><strong>IMPORTANT</strong></strong>E<strong><strong>:</strong></strong></strong></strong> Position Sticky no es compatible con Internet Explorer y versiones anteriores de otros navegadores. Puede consultar el soporte del navegador en<strong><strong> <a href="https://caniuse.com/">caniuse.com</a>.</strong></strong></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2019/10/Ekran-Resmi-2019-10-04-23.09.24.png" class="kg-image" alt="Ekran-Resmi-2019-10-04-23.09.24" width="600" height="400" loading="lazy"><figcaption>Browser Support for Position:sticky</figcaption></figure><hr><p>La mejor manera de entender la propiedad de posición de CSS es mediante la práctica. Continúe codificando hasta que tenga una mejor comprensión. Si algo no está claro, responderé sus preguntas a continuación en la sección de comentarios.</p><p><strong>Si quieres saber más sobre desarrollo web,<strong> </strong>siéntete libre de ¡<a href="https://www.youtube.com/channel/UC1EgYPCvKCXFn8HlpoJwY3Q">seguirme en Youtube!</a></strong></p><p>¡Gracias por leer!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo implementar una aplicación React enrutada en GitHub Pages ]]>
                </title>
                <description>
                    <![CDATA[ Cuando construimos proyectos, queremos mostrarlos en línea. En lugar de comprar un dominio y tomarse el tiempo para configurarlo, es más fácil hospedarlo usando GitHub Pages. Un proyecto que solo usa JavaScript, HTML y CSS es fácil de alojar en GitHub Pages. Sin embargo, los proyectos creados en React, Vue ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/impletacion-de-una-aplicacion-react-en-github-pages/</link>
                <guid isPermaLink="false">62fd49c47b4ec209a40240df</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jorge Sierra ]]>
                </dc:creator>
                <pubDate>Mon, 19 Sep 2022 02:22:13 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2022/08/602aaa1e0a2838549dcc5c67.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/deploy-a-react-app-to-github-pages/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Deploy a Routed React App to GitHub Pages</a>
      </p><p>Cuando construimos proyectos, queremos mostrarlos en línea. En lugar de comprar un dominio y tomarse el tiempo para configurarlo, es más fácil hospedarlo usando GitHub Pages.</p><p>Un proyecto que solo usa JavaScript, HTML y CSS es fácil de alojar en GitHub Pages. Sin embargo, los proyectos creados en React, Vue o Angular requieren algunas configuraciones. Esto le brinda a cualquier persona que visite tu aplicación en línea la misma experiencia que tiene cuando crea la aplicación localmente.</p><p>En este artículo, te mostraré cómo crear una aplicación React simple que use enrutamiento y luego aprenderemos cómo cargarla en GitHub Pages. Daremos especial atención a la parte de enrutamiento, ya que es importante entender e implementar.</p><blockquote>⚠️ Este artículo asume que tienes algún conocimiento de React y Git.</blockquote><h3 id="requisitos-previos">Requisitos previos</h3><p>Debes tener Node, yarn y npm instalados en tu máquina. Para verificar si están instalados, abre una ventana de terminal y escribe lo siguiente:</p><pre><code class="language-bash">npm -v
yarn -v
node -v</code></pre><p>Si estos comandos imprimen un número de versión en la terminal, está listo para comenzar. De lo contrario, debes continuar e instalar lo que falta.</p><ul><li><a href="https://nodejs.org/en/download/">Node</a> (contiene npm)</li><li><a href="https://classic.yarnpkg.com/en/docs/install/#windows-stable">Yarn</a></li></ul><p>También necesitaremos crear un repositorio en <strong><a href="https://github.com/">GitHub</a></strong>. Dirígete a tu cuenta y crea un nuevo repositorio. Elige el nombre que consideres adecuado para este proyecto, pero me quedo con <strong><strong>starter-project<em><em> </em></em></strong></strong>para el resto de este artículo.</p><p>Para crear nuestro proyecto, usaremos <strong><strong>create-react-app</strong></strong>. Es un paquete que te permite crear una aplicación de una sola página con facilidad. Para crear un proyecto, debes escribir lo siguiente en la terminal:</p><pre><code class="language-bash">npx create-react-app starter-project</code></pre><p>Una vez que finalice la operación, tendrás un proyecto React repetitivo, listo para funcionar. Para ver si funciona correctamente, dirígete al directorio del proyecto (en nuestro ejemplo sería starter-project) y ejecuta el comando:</p><pre><code class="language-bash">yarn start</code></pre><p>Si todo funciona correctamente, verás un mensaje en la terminal que dice que tu aplicación se está ejecutando en un servidor local en esta dirección: <a href="http://localhost:3000">http://localhost:3000</a></p><p>Si te diriges allí en tu navegador, verás esto:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/02/IB8uRE3cjN.gif" class="kg-image" alt="IB8uRE3cjN" width="600" height="400" loading="lazy"></figure><h2 id="c-mo-implementar-tu-proyecto-en-github">Cómo implementar tu proyecto en GitHub</h2><p>Es posible que hayas notado que no hemos creado ningún repositorio en GitHub. Entonces, antes de continuar, debemos tener nuestro proyecto cargado allí. Dirígete a tu cuenta de GitHub y crea un repositorio con el mismo nombre que el proyecto React: <strong><strong>starter-project</strong></strong>.</p><blockquote>☝️ Asegúrate de marcar tu repositorio como público. Si lo marca como privado, no podrá usar las GitHub Pages.</blockquote><p>Vamos a añadir este repositorio como remoto a nuestro proyecto. Para hacer eso, en la terminal, escriba:</p><pre><code class="language-bash">git remote add &lt;name-of-remote&gt; &lt;url-of-repository&gt;</code></pre><p>Entonces, en nuestro caso, el comando se ve así:</p><pre><code class="language-bash">git remote add origin https://github.com/TomerPacific/starter-project</code></pre><blockquote>Es importante llamar al control remoto <strong><strong><em><em>origin </em></em></strong></strong>es como se utilizará en nuestro proceso de implementación.</blockquote><p>Después de ejecutar el comando anterior, aún no podemos enviar nuestro código. Primero, necesitamos configurar una rama ascendente y establecer el control remoto como origen.</p><pre><code class="language-bash"> git push --set-upstream origin master</code></pre><p>Ahora, podemos enviar todos los archivos de nuestro proyecto a nuestro repositorio.</p><p>Para que podamos cargar nuestra aplicación creada en GitHub Pages, primero debemos instalar el paquete <a href="https://www.npmjs.com/package/gh-pages">gh-pages</a>.</p><pre><code class="language-bash">yarn add gh-pages</code></pre><p>Este paquete nos ayudará a implementar nuestro código en la rama gh-pages que se usará para alojar nuestra aplicación en GitHub Pages.</p><p>Para permitirnos usar el paquete gh-pages correctamente, necesitamos agregar dos claves al valor de nuestros scripts en el archivo package.json:</p><pre><code class="language-package">"scripts": {
    "start": "react-scripts start",
    "predeploy": "npm run build", &lt;----------- #1
    "deploy": "gh-pages -d build", &lt;---------- #2
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },</code></pre><p>A continuación, debemos modificar nuestro archivo package.json agregando el campo de la página de inicio. React utiliza este campo para averiguar la <strong>URL</strong> raíz en el archivo <strong>HTML</strong> creado. En él pondremos la <strong>URL</strong> de nuestro repositorio de <strong>GitHub</strong>.</p><pre><code class="language-package">{
  "name": "starter-project",
  "homepage": "https://tomerpacific.github.io/starter-project/", &lt;----
  "version": "0.1.0",
  /....
}</code></pre><p>Para desplegar nuestra aplicación, escribe lo siguiente en la terminal:</p><pre><code class="language-bash">npm run deploy</code></pre><p>Ejecutar el comando anterior se encarga de compilar tu aplicación y enviarla a una rama llamada gh-pages, que GitHub usa para vincularse con las páginas de GitHub.</p><blockquote>? Si no nombraste tu control remoto <strong><strong><em><em>origin</em></em></strong></strong>, obtendrás un error durante esta fase que indica que: <strong><strong>Failed to get remote.origin.url (task must either be run in a git repository with a configured origin remote or must be configured with the "repo" option)</strong></strong>.</blockquote><p>Sabrás que el proceso fue exitoso si al final del mismo ves la palabra Publicado. Ahora podemos dirigirnos a nuestro repositorio de GitHub en Configuración y desplazarnos hacia abajo hasta la sección Páginas de GitHub.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2021/02/chrome_egdTtIso1X.png" class="kg-image" alt="chrome_egdTtIso1X" width="600" height="400" loading="lazy"></figure><p>Si ves un mensaje similar al anterior, significa que tu aplicación ahora está alojada correctamente en las GitHub Pages.</p><h2 id="enrutamiento-en-react">Enrutamiento en React</h2><p>Hasta ahora:</p><ol><li>Tenemos una aplicación básica de React que está alojada en GitHub Pages</li><li>También tenemos un proceso simplificado para implementarlo cuando queremos hacer cambios.</li></ol><p>Pero dado que el propósito de este artículo es mostrar una aplicación más compleja que la que creamos inicialmente, analizaremos el enrutamiento.</p><p>Un componente que falta en nuestra aplicación es la navegación. Nuestra aplicación no será solo una página, probablemente tendrá muchas páginas. Entonces, ¿cómo podrán los usuarios navegar entre ellos?</p><p>React es una biblioteca y no contiene todo lo que necesitas para su aplicación lista para usar (en nuestro caso, enrutamiento). Por lo tanto, necesitaremos instalar <a href="https://reactrouter.com/web/guides/quick-start">react router</a>.</p><p>React router tiene diferentes componentes para aplicaciones web y para aplicaciones nativas. Como estamos construyendo una aplicación web, usaremos <strong><strong>react-router-dom</strong></strong>.</p><pre><code class="language-bash">yarn add react-router-dom</code></pre><p>Para hacer uso del enrutamiento en nuestra aplicación, creemos un elemento de navegación que estará visible en la parte superior de la aplicación. Agregaremos esto dentro de nuestro archivo App.js y reemplazaremos el marcado HTML actual que está allí.</p><pre><code class="language-html"> &lt;div&gt;
     &lt;nav&gt;
         &lt;ul id="navigation"&gt;
             &lt;li&gt;
                 &lt;Link to="/"&gt;Home&lt;/Link&gt;
             &lt;/li&gt;
             &lt;li&gt;
                 &lt;Link to="/about"&gt;About&lt;/Link&gt;
             &lt;/li&gt;
             &lt;li&gt;
                 &lt;Link to="/contact"&gt;Contact&lt;/Link&gt;
             &lt;/li&gt;
         &lt;/ul&gt;
     &lt;/nav&gt;
&lt;/div&gt;</code></pre><p>Por lo general, en un proyecto que no sea de React, colocaríamos una ruta relativa a nuestras páginas HTML para cada sección. De esa forma, el navegador sabe desde dónde cargar los datos.</p><p>Pero en nuestro proyecto, no tendremos diferentes páginas HTML para cada sección. Simplemente, cargaremos un componente diferente. El marcado que solía estar dentro de App.js ahora se encontrará dentro de un componente llamado <strong>Home</strong>.</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">import './App.css';
import React from 'react';
import logo from './logo.svg';

class Home extends React.Component {
    render() {
        return (
            &lt;div className="App"&gt;
                &lt;header className="App-header"&gt;
                &lt;img src={logo} className="App-logo" alt="logo" /&gt;
                &lt;p&gt;
                    Edit &lt;code&gt;src/App.js&lt;/code&gt; and save to reload.
                &lt;/p&gt;
                &lt;a
                    className="App-link"
                    href="https://reactjs.org"
                    target="_blank"
                    rel="noopener noreferrer"
                &gt;
                    Learn React
                &lt;/a&gt;
                &lt;/header&gt;
            &lt;/div&gt;
            
        );
    }

}

export default Home;</code></pre><figcaption>Home.jsx</figcaption></figure><p>Como hemos creado tres secciones en nuestra navegación y nos hemos ocupado de la sección de inicio, vamos a dar otro ejemplo con la sección Acerca de.</p><p>Crearemos un nuevo archivo llamado <strong>About.jsx</strong> que contendrá nuestra plantilla y código para la sección acerca de.</p><pre><code class="language-javascript">import React from 'react';

const divStyle = {
    color:'white'
};

class About extends React.Component {
    
    render() {
        return (
            &lt;div style={divStyle}&gt;
                &lt;h2&gt;About Page&lt;/h2&gt;
                &lt;main&gt;
                    &lt;p&gt;This section contains information about...&lt;/p&gt;
                &lt;/main&gt;
            &lt;/div&gt;
        )
    }
}



export default About;</code></pre><p>Puede que te preguntes, ¿cómo sabrá la aplicación que debe redirigir al usuario una vez que haga clic en el enlace Acerca de? Para eso usaremos un componente llamado <strong><strong>Route</strong></strong>.</p><p>La ruta es uno de los componentes más importantes en react-router porque te permite representar diferentes componentes según la ruta de la URL. Para nuestro proyecto, usaremos el siguiente código dentro de App.js justo debajo del marcado de navegación.</p><pre><code class="language-html">&lt;Switch&gt;
    &lt;Route exact path="/"&gt;
    &lt;Home /&gt;
    &lt;/Route&gt;
    &lt;Route path="/about"&gt;
    &lt;About /&gt;
    &lt;/Route&gt;
&lt;/Switch&gt;</code></pre><p>Puedes ver que creamos dos rutas para Home y About. El componente Switch nos permite agrupar los componentes de la ruta y solo coincidirá con uno de ellos.</p><p>Nuestro archivo App.js combinado se ve así:</p><pre><code class="language-javascript">import './App.css';
import React from 'react';
import { Route, Switch, Link } from "react-router-dom";
import About from './About';
import Home from './Home';

class App extends React.Component {
  render() {
      return (
        &lt;div className="App"&gt;
          &lt;div&gt;
            &lt;nav&gt;
              &lt;ul id="navigation"&gt;
                &lt;li&gt;
                  &lt;Link to="/"&gt;Home&lt;/Link&gt;
                &lt;/li&gt;
                &lt;li&gt;
                &lt;Link to="/about"&gt;About&lt;/Link&gt;
                &lt;/li&gt;
                &lt;li&gt;
                &lt;Link to="/contact"&gt;Contact&lt;/Link&gt;
                &lt;/li&gt;
              &lt;/ul&gt;
            &lt;/nav&gt;
          &lt;/div&gt;
            &lt;Switch&gt;
            &lt;Route exact path="/"&gt;
              &lt;Home /&gt;
            &lt;/Route&gt;
            &lt;Route path="/about"&gt;
              &lt;About /&gt;
            &lt;/Route&gt;
          &lt;/Switch&gt;
          &lt;/div&gt;
            );
  }
}

export default App;
</code></pre><p>Una última cosa que debemos hacer es envolver todo nuestro proyecto en un componente de enrutador. Necesitamos hacer esto porque nos permite usar el enrutamiento en nuestra aplicación. Usaremos el componente BrowserRouter, ya que usa la API de historial de HTML5.</p><figure class="kg-card kg-code-card"><pre><code class="language-html">ReactDOM.render(
  &lt;React.StrictMode&gt;
    &lt;BrowserRouter&gt;
      &lt;App /&gt;
    &lt;/BrowserRouter&gt;
  &lt;/React.StrictMode&gt;,
  document.getElementById('root')
);</code></pre><figcaption>index.js</figcaption></figure><p>Si ejecutamos las cosas localmente, todo parece funcionar. Implementemos nuestro proyecto en GitHub Pages y veamos cuál es el resultado.</p><h2 id="c-mo-manejar-el-enrutamiento-usando-hashrouter">Cómo manejar el enrutamiento usando HashRouter</h2><p>A primera vista, todo parece funcionar bien. Pero cuando intente actualizar la página o navegar a través del navegador, seguirá recibiendo errores 404.</p><p>¿Por qué pasó esto? Porque las Páginas de GitHub no admiten el historial del navegador como lo hace su navegador. En nuestro caso, la ruta <a href="https://tomerpacific.github.io/starter-project/about"><strong>https://tomerpacific.github.io/starter-project/about</strong></a> no ayuda a las páginas de GitHub a comprender dónde apuntar al usuario (ya que es una ruta de interfaz).</p><p>Para superar este problema, necesitamos usar un Hash Router en lugar de un Browser Router en nuestra aplicación. Este tipo de enrutador usa la parte hash de la URL para mantener la interfaz de usuario sincronizada con la URL.</p><figure class="kg-card kg-code-card"><pre><code class="language-html">ReactDOM.render(
  &lt;React.StrictMode&gt;
    &lt;HashRouter&gt;
      &lt;App /&gt;
    &lt;/HashRouter&gt;
  &lt;/React.StrictMode&gt;,
  document.getElementById('root')
);</code></pre><figcaption>index.js</figcaption></figure><p>Puedes leer más sobre esto <a href="https://create-react-app.dev/docs/deployment/#github-pages-https-pagesgithubcom"><strong>aquí.</strong></a></p><p>Vuelve a implementar tu aplicación y estarás satisfecho con el resultado. No más errores 404.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo implementar la renderización del lado del servidor en tu aplicación React en tres simples pasos ]]>
                </title>
                <description>
                    <![CDATA[ Escrito por Rohit Kumar Esto es lo que construiremos en este tutorial: una buena tarjeta React como esta. En este tutorial, utilizaremos la renderización del lado del servidor para ofrecer una respuesta HTML cuando un usuario o rastreador accede a la URL de una página. Manejaremos las últimas solicitudes en ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/renderizacion-leteral-en-tu-aplicacion-react-en-tres-sencillos-pasos/</link>
                <guid isPermaLink="false">62f5322b7b4ec209a40232df</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jorge Sierra ]]>
                </dc:creator>
                <pubDate>Sat, 10 Sep 2022 15:45:35 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2022/08/5f9c9c5d740569d1a4ca31bc.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/server-side-rendering-your-react-app-in-three-simple-steps-7a82b95db82e/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to implement server-side rendering in your React app in three simple steps</a>
      </p><p>Escrito por Rohit Kumar</p><p>Esto es lo que construiremos en este tutorial: una buena tarjeta React como esta.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2020/03/1_wk04sWGQkw36_XLFvPACrA-1.png" class="kg-image" alt="1_wk04sWGQkw36_XLFvPACrA-1" width="600" height="400" loading="lazy"></figure><p>En este tutorial, utilizaremos la renderización del lado del servidor para ofrecer una respuesta HTML cuando un usuario o rastreador accede a la URL de una página. Manejaremos las últimas solicitudes en el lado del cliente.</p><p>¿Por qué lo necesitamos?</p><p>Déjame guiarte a la respuesta.</p><h2 id="-cu-l-es-la-diferencia-entre-la-representaci-n-del-lado-del-cliente-y-la-representaci-n-del-lado-del-servidor">¿Cuál es la diferencia entre la representación del lado del cliente y la representación del lado del servidor?</h2><p>En la<strong><strong> </strong>renderización del lado del cliente<strong>, </strong></strong>tu navegador descarga una página HTML mínima. Representa el JavaScript y llena el contenido en él.</p><p><strong>Renderización del lado del servidor<strong>, </strong></strong>por otro lado, renderiza los componentes de React en el servidor. La salida es contenido HTML.</p><p>Puede combinar estos dos para crear una aplicación isomorfa.</p><h2 id="contras-de-renderizar-react-en-el-servidor">Contras de renderizar React en el servidor</h2><ul><li>SSR puede mejorar el rendimiento si su aplicación es pequeña. Pero también puede degradar el rendimiento si es pesado.</li><li>Aumenta el tiempo de respuesta (y puede ser peor si el servidor está ocupado).</li><li>Aumenta el tamaño de la respuesta, lo que significa que la página tarda más en cargarse.</li><li>Aumenta la complejidad de la aplicación.</li></ul><h2 id="-cu-ndo-deber-a-utilizar-la-representaci-n-del-lado-del-servidor">¿Cuándo debería utilizar la representación del lado del servidor?</h2><p>A pesar de estas consecuencias de SSR, hay algunas situaciones en las que puede y debe usarlo.</p><h3 id="1-seo"><strong>1. SEO</strong></h3><p>Todos los sitios web quieren aparecer en las búsquedas. Corrígeme si me equivoco.</p><p>Desafortunadamente, los rastreadores de los motores de búsqueda aún no entienden o procesan JavaScript.</p><p>Esto significa que ven una página en blanco, sin importar cuán útil sea su sitio.</p><p>Mucha gente dice que el rastreador de Google ahora muestra JavaScript.</p><p>Para probar esto, implementé la aplicación en Heroku. Esto es lo que vi en la consola de búsqueda de Google:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*KgOtUd6XBbeZvR1FDBGcXA.png" class="kg-image" alt="1*KgOtUd6XBbeZvR1FDBGcXA" width="800" height="537" loading="lazy"><figcaption>Google’s crawler does not render React</figcaption></figure><p>Una página en blanco.</p><p>Esta fue la principal razón por la que exploré el renderizado del lado del servidor. Especialmente cuando se trata de una página fundamental, como una página de destino, un blog, etc.</p><p>Para verificar si Google renderiza tu sitio, visite:</p><p>Panel de la Consola de Búsqueda &gt; Inspeccionar URL &gt; Explorar como Google. Ingrese la URL de la página o déjela vacía para la página de inicio.</p><p>Selecciona BUSCAR Y RENDERIZAR. Una vez completado, haga clic para ver el resultado.</p><h3 id="2-mejorar-el-rendimiento"><strong>2. </strong>Mejorar el rendimiento</h3><p>En SSR, el rendimiento de la aplicación depende de los recursos del servidor y la velocidad de la red del usuario. Esto lo hace muy útil para sitios con mucho contenido.</p><p>Por ejemplo, supongamos que tienes un teléfono móvil de precio medio con una velocidad de internet lenta. Intenta acceder a un sitio que descarga 4 MB de datos antes de poder ver nada.</p><p>¿Serías capaz de ver algo en tu pantalla dentro de 2 a 4 segundos?</p><p>¿Volverías a visitar ese sitio?</p><p>No creo que lo harías.</p><p>Otra mejora importante es el tiempo de interacción con el primer usuario. Esta es la diferencia en el tiempo desde que un usuario accede a la URL hasta que ve el contenido.</p><p>Aquí está la comparación. Lo probé en una Mac de desarrollo.</p><h4 id="react-renderizado-en-el-servidor"><strong>React </strong>renderizado en el servidor</h4><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*kYMHoa7OemCHA_KBzJ1w-w.png" class="kg-image" alt="1*kYMHoa7OemCHA_KBzJ1w-w" width="800" height="347" loading="lazy"><figcaption>SSR performance report (Chrome)</figcaption></figure><p>El tiempo de la primera interacción es de 300ms. Hidratar los acabados a los 400ms. El evento de carga sale a los 500ms aproximadamente. Puedes ver esto mirando la imagen de arriba.</p><h4 id="react-renderizado-en-el-navegador-del-cliente"><strong>React</strong> renderizado en el navegador del cliente</h4><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*wquRCRboPDi7Ix2HAxvCAA.png" class="kg-image" alt="1*wquRCRboPDi7Ix2HAxvCAA" width="800" height="342" loading="lazy"><figcaption>Client side performance report (Chrome)</figcaption></figure><p>El tiempo de la primera interacción es de 400ms. El evento de carga sale a los 470 ms.</p><p>El resultado habla por sí mismo. Hay una diferencia de 100 ms en el tiempo de interacción del primer usuario para una aplicación tan pequeña.</p><h3 id="-c-mo-funciona-4-pasos-simples-">¿Cómo funciona? — (4 pasos simples)</h3><ul><li>Cree una tienda Redux nueva en cada solicitud.</li><li>Opcionalmente, envíe algunas acciones.</li><li>Saque el estado de la tienda y almacena realiza SSR.</li><li>Envía el estado obtenido en el paso anterior junto con la respuesta.</li></ul><p>Usaremos el estado pasado en la respuesta para crear el estado inicial en el lado del cliente.</p><p>Antes de comenzar, <a href="https://github.com/llamaeager/ssr">clone o descargue el ejemplo completo de Github</a> y utilícelo como referencia.</p><h3 id="primeros-pasos-configurando-nuestra-aplicaci-n">Primeros pasos configurando nuestra aplicación</h3><p>Primero, abra su editor y Shell favoritos. Cree una nueva carpeta para su aplicación. Empecemos.</p><pre><code class="language-bash">npm init --yes</code></pre><p>Complete los detalles. Después es generado <code>package.json</code>, copie las dependencias y scripts a continuación en él.</p><p>Instale todas las dependencias ejecutando:</p><pre><code class="language-bash">npm install</code></pre><p>Ahora debe configurar Babel y webpack para que nuestro script de compilación funcione.</p><p>Babel transforma ESM y React en código que el navegador pueda entender.</p><p>Crear un nuevo archivo <code>.babelrc</code> y poner la línea de abajo en él.</p><pre><code class="language-js">{
  "presets": ["@babel/env", "@babel/react"]
}
</code></pre><p>webpack agrupa nuestra aplicación y sus dependencias en un solo archivo. Crear otro archivo <code>webpack.config.js </code>con el siguiente código en él:</p><pre><code class="language-js">const path = require('path');module.exports = {
    entry: {
        client: './src/client.js',
        bundle: './src/bundle.js'
    },
    output: {
        path: path.resolve(__dirname, 'assets'),
        filename: "[name].js"
    },
    module: {
        rules: [
            { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" }
        ]
    }
}</code></pre><p>Los dos archivos de salida del proceso de compilación:</p><ol><li><code>assets/bundle.js</code> — aplicación pura del lado del cliente.</li><li><code>assets/client.js</code> — Aplicación del lado del servidor SSR.</li></ol><p>La <code>src/ </code>carpeta contiene el código fuente. Los archivos compilados de Babel van en <code>views/</code>. <code>views</code> el directorio se creará automáticamente si no está presente.</p><h3 id="-por-qu-necesitamos-compilar-archivos-fuente">¿Por qué necesitamos compilar archivos fuente?</h3><p>Desafortunadamente, React no funcionan en Node. Aquí viene Babel al rescate. El siguiente script le dice a Babel que compile todos los archivos en la carpeta <code>src</code> y ponga el resultado en la carpeta <code>views.</code></p><pre><code class="language-json">"babel": "babel src -d views",</code></pre><p>Ahora, Node puede ejecutarlos.</p><h3 id="copia-archivos-precodificados-y-est-ticos">Copia archivos precodificados y estáticos</h3><p>Si ya ha clonado el repositorio, cópielo. De lo contrario, descargue el archivo ssr-static.zip de Dropbox. Extráigalo y mantenga estas tres carpetas dentro del directorio de su aplicación. Esto es lo que contienen.</p><ol><li>React <code>App</code> y los componentes residen en <code>src/components</code>.</li><li>Archivos redux en <code>src/redux/</code>.</li><li><code>assets/ &amp; media/</code>: Contiene archivos estáticos como <code>style.css</code> y las imágenes.</li></ol><h3 id="lado-del-servidor">Lado del servidor</h3><p>Crear dos nuevos archivos llamados <code>server.js</code> y <code>template.js</code> dentro de <code>src/</code> carpeta.</p><h3 id="1-src-server-js"><strong>1. src/server.js</strong></h3><p>La magia sucede aquí. Este es el código que has estado buscando.</p><pre><code class="language-jsx">import React from 'react';
import { renderToString } from 'react-dom/server';
import { Provider } from 'react-redux';
import configureStore from './redux/configureStore';
import App from './components/app';

module.exports = function render(initialState) {
  // Model the initial state  
  const store = configureStore(initialState);
  let content = renderToString(&lt;Provider store={store} &gt;&lt;App /&gt;&lt;/Provider&gt;);
  const preloadedState = store.getState();
  return {
    content,
    preloadedState
  };
};</code></pre><p>En lugar de representar nuestra aplicación, debemos envolverla en una función y exportarla. La función acepta el estado inicial de la aplicación.</p><p>Así es como funciona.</p><ol><li>Pasa <code>initialState</code> a la función <code>configureStore()</code>. <code>configureStore()</code>devuelve una nueva instancia de Store. Lo almacena dentro de la variable <code>store</code>.</li><li>Llama el método <code>renderToString()</code>, proporciona nuestra aplicación como entrada. Representa nuestra aplicación en el servidor y devuelve el HTML producido. Ahora, la variable <code>content</code> almacena el HTML.</li><li>Saca el estado de Redux Store llamando <code>getState()</code> en <code>store</code>. Guárdalo en una variable <code>preloadedState</code>.</li><li>Devuelve <code>content</code> y <code>preloadedState</code>. Los pasaremos a nuestra plantilla para obtener la página HTML final.</li></ol><h4 id="2-src-template-js"><strong><code>2. src/template.js</code></strong></h4><p><code>template.js</code> exporta una función. Se necesita <code>title</code>, <code>state</code> y <code>content</code> como entrada. Los inyecta en la plantilla y devuelve el documento HTML final.</p><p>Para pasar a lo largo del estado, la plantilla adjunta <code>state</code> a <code>window.__STATE__</code> dentro de una <code>&lt;script&gt;i</code> etiqueta.</p><p>Ahora puedes leer <code>state</code> en el lado del cliente accediendo <code>window.__STATE__</code>.</p><p>También incluimos el compañero SSR <code>assets/client.js</code> aplicación del lado del cliente en otra etiqueta de secuencia de comandos.</p><p>Si solicita la versión de cliente puro, solo pone <code>assets/bundle.js</code> dentro de la etiqueta del script.</p><h3 id="el-lado-del-cliente">El lado del cliente</h3><p>El lado del cliente es bastante sencillo.</p><h3 id="1-src-bundle-js"><strong>1. src/bundle.js</strong></h3><p>Así es como se escribe React y Redux <code>Provider</code> envuelto. Es nuestra aplicación pura del lado del cliente. No hay trucos aquí.</p><pre><code class="language-jsx">import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import configureStore from './redux/configureStore';
import App from './components/app';

const store = configureStore();
render(
  &lt;Provider store={store} &gt; &lt;App /&gt; &lt;/Provider&gt;,
  document.querySelector('#app')
);</code></pre><h3 id="2-src-client-js"><strong>2. src/client.js</strong></h3><p>¿Luce familiar? Sí, no hay nada especial, excepto <code>window.__STATE__.</code> Todo lo que tenemos que hacer es tomar el estado inicial de <code>window.__STATE__</code> Y pasarlo a nuestra función <code>configureStore()</code> como el estado inicial.</p><p>Echemos un vistazo a nuestro nuevo archivo de cliente:</p><pre><code class="language-jsx">import React from 'react';
import { hydrate } from 'react-dom';
import { Provider } from 'react-redux';
import configureStore from './redux/configureStore';
import App from './components/app';

const state = window.__STATE__;
delete window.__STATE__;
const store = configureStore(state);
hydrate(
  &lt;Provider store={store} &gt; &lt;App /&gt; &lt;/Provider&gt;,
  document.querySelector('#app')
);</code></pre><p>Repasemos los cambios:</p><ol><li>Reemplaza <code>render()</code> con <code>hydrate()</code>. <a href="https://reactjs.org/docs/react-dom.html#hydrate" rel="noopener"><code>hydrate()</code></a> es lo mismo que <code>render(), pero</code> se utiliza para hidratar elementos producidos por <a href="https://reactjs.org/docs/react-dom-server.html" rel="noopener"><code>ReactDOMServer</code></a>. Garantiza que el contenido sea el mismo en el servidor y en el cliente.</li><li>Lee el estado del objeto en window object <code>window.__STATE__</code>. Guárdelo en una variable y elimine el <code>window.__STATE__</code>.</li><li>Crea una nueva tienda con <code>state</code> como estado inicial.</li></ol><p>Todo hecho aquí.</p><h2 id="poni-ndolo-todo-junto">Poniéndolo todo junto</h2><h3 id="index-js"><strong>Index.js</strong></h3><p>Este es el punto de entrada de nuestra aplicación. Maneja solicitudes y plantillas.</p><p>También declara una variable<code>initialState</code>. Lo he modelado con datos en el archivo <code>assets/data.json </code>. La pasaremos a nuestra función <code>ssr()</code>.</p><p><em><em>Not</em>a<em>: </em></em>Al hacer referencia a un archivo que está dentro<em><em> </em></em><code><em><em>src/</em></em></code><em><em> </em></em>de un archivo fuera<em><em> </em></em><code><em><em>src/</em></em></code><em><em>, </em></em>uso normal <code><em><em>require()</em></em></code><em><em> </em></em>y reemplace<em><em> </em></em><code><em><em>src/</em></em></code><em><em> </em>por<em> </em></em><code><em><em>views/</em></em></code><em><em>. </em></em>tu sabes la razon<em><em> (</em>compila <em>Babel).</em></em></p><p>Enrutamiento</p><ol><li><code>/</code>: Por defecto, la página de inicio renderizada por el servidor.</li><li><code>/client</code>: Ejemplo puro de representación del lado del cliente.</li><li><code>/exit</code>: Botón de parada del servidor. Solo disponible en desarrollo.</li></ol><h4 id="construir-y-ejecuta">Construir y ejecuta</h4><p>Es hora de construir y ejecutar nuestra aplicación. Podemos hacer esto con una sola línea de código.</p><pre><code class="language-bash">npm run build &amp;&amp; npm run start</code></pre><p>Ahora, la aplicación se está ejecutando en <a href="http://localhost:3000/" rel="noopener">http://localhost:3000</a>.</p><h3 id="-listo-para-convertirte-en-un-pro-en-react">¿Listo para convertirte en un Pro en React?</h3><p>Comenzaré una nueva serie a partir del próximo lunes para que tus habilidades de React ardan de inmediato.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*TEecv1nLg253xmyGgddhOw.gif" class="kg-image" alt="1*TEecv1nLg253xmyGgddhOw" width="500" height="226" loading="lazy"><figcaption>subscription link below ?</figcaption></figure><h3 id="gracias-por-leer-este-tutorial">Gracias por leer este tutorial</h3> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Ejemplos de subcadenas de JavaScript: Métodos Slice, Substr y Substring en JS ]]>
                </title>
                <description>
                    <![CDATA[ En la programación diaria, a menudo necesitamos trabajar con cadenas. Afortunadamente, hay muchos métodos incorporados en JavaScript que nos ayudan mientras trabajaban con matrices, cadenas y otros tipos de datos. Podemos usar estos métodos para varias operaciones como buscar, reemplazar, concatenar cadenas, etc. Obtener una subcadena de una cadena es ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/ejemplos-de-subcadenas-de-javascript-metodos-slice-substr-substring-en-js/</link>
                <guid isPermaLink="false">62f50e8db4def50851977f15</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jorge Sierra ]]>
                </dc:creator>
                <pubDate>Sat, 10 Sep 2022 15:11:13 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2022/08/5f9c9c0d740569d1a4ca2fa6.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/javascript-substring-examples-slice-substr-and-substring-methods-in-js/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">JavaScript Substring Examples - Slice, Substr, and Substring Methods in&nbsp;JS</a>
      </p><p>En la programación diaria, a menudo necesitamos trabajar con cadenas. Afortunadamente, hay muchos métodos incorporados en JavaScript que nos ayudan mientras trabajaban con matrices, cadenas y otros tipos de datos. Podemos usar estos métodos para varias operaciones como buscar, reemplazar, concatenar cadenas, etc.</p><p>Obtener una subcadena de una cadena es una de las operaciones más comunes en JavaScript. En este artículo, aprenderá cómo obtener una subcadena mediante el uso de 3 métodos integrados diferentes. Pero primero, déjame explicarte qué es probable que sea una subcadena.</p><h3 id="-qu-es-una-subcadena"><strong>¿Qué es una subcadena?</strong></h3><p>Una subcadena es un subconjunto de otra cadena:</p><pre><code class="language-javascript">"I am learning JavaScript and it is cool!"  --&gt;  Original String

"I am learning JavaScript"  --&gt;  Substring

"JavaScript is cool!"  --&gt;  Another Substring</code></pre><p>Como en el ejemplo anterior, en algunos casos necesitamos obtener una o más subcadenas de una oración completa o un párrafo. Ahora veamos cómo hacer eso en JavaScript de 3 maneras diferentes.</p><h2 id="1-el-m-todo-de-la-subcadena-"><strong>1. El método de la subcadena()</strong></h2><p>Empecemos con el método substring(). Este método básicamente obtiene una parte de la cadena original y la devuelve como una nueva cadena. El método de subcadena espera dos parámetros:</p><pre><code class="language-javascript">string.substring(startIndex, endIndex);</code></pre><ul><li><strong><strong>startIndex</strong></strong> : representa el punto de inicio de la subcadena</li><li><strong><strong>endIndex</strong></strong> : representa el punto final de la subcadena (opcional)</li></ul><p>Veamos el uso en un ejemplo. Supongamos que tenemos la siguiente cadena de ejemplo:</p><pre><code class="language-javascript">const myString = "I am learning JavaScript and it is cool!";</code></pre><p>Ahora, si configuramos startIndex como 0 y endIndex como 10, obtendremos los primeros 10 caracteres de la cadena original:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2020/03/Ekran-Resmi-2020-03-21-19.17.10.png" class="kg-image" alt="Ekran-Resmi-2020-03-21-19.17.10" width="600" height="400" loading="lazy"><figcaption><strong style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 700; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 17.6px; vertical-align: baseline; color: var(--gray85);">The first character's index is always 0</strong></figcaption></figure><p>Sin embargo, si establecemos solo un índice inicial y ningún índice final para este ejemplo:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2020/03/Ekran-Resmi-2020-03-21-19.16.46.png" class="kg-image" alt="Ekran-Resmi-2020-03-21-19.16.46" width="600" height="400" loading="lazy"></figure><p>Luego obtenemos una subcadena que comienza desde el sexto carácter hasta el final de la cadena original.</p><p>Algunos puntos adicionales:</p><ul><li>Si startIndex = endIndex, el método de subcadena devuelve una cadena vacía</li><li>Si startIndex y endIndex son mayores que la longitud de la cadena, devuelve una cadena vacía</li><li>Si startIndex &gt; endIndex, el método de subcadena intercambia los argumentos y devuelve una subcadena, asumiendo que endIndex &gt; startIndex</li></ul><h2 id="2-el-m-todo-slice-"><strong>2. El método slice( ) </strong></h2><p>El método slice( ) es similar al método substring( ) y también devuelve una subcadena de la cadena original. El método slice() también espera los mismos dos parámetros:</p><pre><code class="language-javascript">string.slice(startIndex, endIndex);</code></pre><ul><li><strong><strong>startIndex</strong></strong>: representa el punto de partida de la subcadena</li><li><strong><strong>endIndex</strong></strong>: representa el punto final de la subcadena (opcional)</li></ul><h4 id="los-puntos-comunes-de-los-m-todos-substring-y-slice-">Los puntos comunes de los métodos substring() y slice():</h4><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2020/03/Ekran-Resmi-2020-03-22-01.03.15.png" class="kg-image" alt="Ekran-Resmi-2020-03-22-01.03.15" width="600" height="400" loading="lazy"></figure><ul><li>Si configuramos startIndex y endIndex, obtendremos los caracteres entre los números de índice dados de la cadena original:</li></ul><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2020/03/Ekran-Resmi-2020-03-22-01.03.43.png" class="kg-image" alt="Ekran-Resmi-2020-03-22-01.03.43" width="600" height="400" loading="lazy"></figure><ul><li>Si startIndex y endIndex son mayores que la longitud de la cadena, devuelve una cadena vacía</li></ul><h4 id="diferencias-del-m-todo-slice-">Diferencias del método slice():</h4><ul><li>Si startIndex es un número negativo, entonces el primer carácter comienza desde el final de la cadena (al revés):</li></ul><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2020/03/Ekran-Resmi-2020-03-22-15.54.09.png" class="kg-image" alt="Ekran-Resmi-2020-03-22-15.54.09" width="600" height="400" loading="lazy"></figure><blockquote><strong><strong>Nota:</strong></strong> Podemos usar el método slice() también para arreglos de JavaScript.</blockquote><h2 id="3-el-m-todo-substr-"><strong>3. El método substr()</strong></h2><p><a href="https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/String/substr">Según los documentos de Mozilla</a> , el método substr() se considera una función heredada y se debe evitar su uso. Pero aún explicaré brevemente lo que hace porque es posible que lo vea en proyectos más antiguos.</p><p>El método substr( ) también devuelve una subcadena de la cadena original y espera dos parámetros como:</p><pre><code class="language-javascript">string.substring(startIndex, length);</code></pre><ul><li><strong><strong>startIndex</strong></strong> : representa el punto de inicio de la subcadena</li><li><strong><strong>l</strong>ength</strong>: número de caracteres a incluir (opcional)</li></ul><p>Puede ver la diferencia aquí: el método substr() espera que el segundo parámetro sea una longitud en lugar de un índice final:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2020/03/Ekran-Resmi-2020-03-22-00.40.29-2.png" class="kg-image" alt="Ekran-Resmi-2020-03-22-00.40.29-2" width="600" height="400" loading="lazy"></figure><p>En este ejemplo, básicamente cuenta 5 caracteres que comienzan con el índice de inicio dado y los devuelve como una subcadena.</p><p>Sin embargo, si no definimos el segundo parámetro, regresa hasta el final de la cadena original (como lo hacen los dos métodos anteriores):</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2020/03/Ekran-Resmi-2020-03-22-00.40.23.png" class="kg-image" alt="Ekran-Resmi-2020-03-22-00.40.23" width="600" height="400" loading="lazy"></figure><blockquote><strong><strong>Nota:</strong></strong> Los 3 métodos devuelven la subcadena como una nueva cadena y no cambian la cadena original.</blockquote><h2 id="conclusi-n">Conclusión</h2><p>Estos son los 3 métodos diferentes para obtener una subcadena en JavaScript. Hay muchos otros métodos incorporados en JS que realmente nos ayudan mucho cuando tratamos con varias cosas en la programación. Si encuentra útil esta publicación, compártala en las redes sociales.</p><p><strong><strong>Si quieres aprender más sobre desarrollo web, ¡no dudes en <a href="https://www.youtube.com/channel/UC1EgYPCvKCXFn8HlpoJwY3Q" rel="noopener">seguirme en Youtube</a> !</strong></strong></p><p>¡Gracias por leer!</p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
