One place for hosting & domains

      personalizados

      Cómo crear componentes personalizados en React


      El autor seleccionó Creative Commons para recibir una donación como parte del programa Write for DOnations.

      Introducción

      En este tutorial, aprenderá a crear componentes personalizados en React. Los componentes son elementos de funcionalidad independientes que puede reutilizar en sus aplicaciones, y son las unidades estructurales básicas de todas las aplicaciones de React. A menudo, pueden ser simples clases y funciones de JavaScript, pero se los utiliza como si fueran elementos HTML personalizados. Los botones, los menús y cualquier otro contenido de las páginas de front-end pueden crearse como componentes. Los componentes también pueden contener información de estado y mostrar marcado.

      Una vez que aprenda a crear componentes en React, podrá dividir aplicaciones complejas en pequeñas piezas más fáciles de compilar y mantener.

      En este tutorial, creará una lista de emojis que mostrarán sus nombres cuando se hace clic en ellos. Los emojis se crearán usando un componente personalizado y se los invocará desde el interior de otro componente personalizado. Al final de este tutorial, habrá creado componentes personalizados utilizando clases y funciones de JavaScript, y sabrá cómo separar código existente en elementos reutilizables y cómo almacenar los componentes en una estructura de archivos legible.

      Requisitos previos

      Paso 1: Configurar el proyecto React

      En este paso, creará una base para su proyecto usando Create React App. También modificará el proyecto predeterminado para crear su proyecto de base al asignar una lista de emojis y añadir algo de estilo.

      Primero, cree un proyecto nuevo. Abra un terminal y, luego, ejecute el siguiente comando:

      • npx create-react-app tutorial-03-component

      Una vez completado, pase al directorio del proyecto:

      Abra el código App.js en un editor de texto:

      A continuación, elimine el código de la plantilla creado por Create React App y sustituya el contenido por el nuevo código de React que muestra una lista de emojis:

      tutorial-03-component/src/App.js

      import React from 'react';
      import './App.css';
      
      const displayEmojiName = event => alert(event.target.id);
      const emojis = [
        {
          emoji: '😀',
          name: "test grinning face"
        },
        {
          emoji: '🎉',
          name: "party popper"
        },
        {
          emoji: '💃',
          name: "woman dancing"
        }
      ];
      
      function App() {
        const greeting = "greeting";
        const displayAction = false;
        return(
          <div className="container">
            <h1 id={greeting}>Hello, World</h1>
            {displayAction && <p>I am writing JSX</p>}
            <ul>
              {
                emojis.map(emoji => (
                  <li key={emoji.name}>
                    <button
                      onClick={displayEmojiName}
                    >
                      <span role="img" aria-label={emoji.name} id={emoji.name}>{emoji.emoji}</span>
                    </button>
                  </li>
                ))
              }
            </ul>
          </div>
        )
      }
      
      export default App;
      

      Este código utiliza la sintaxis de JSX para hacer map() sobre la matriz de emojis y enumerarlos como elementos de la lista <li>. También adjunta eventos onClick para mostrar datos de los emojis en el navegador. Para analizar el código con mayor detenimiento, consulte Cómo crear elementos de React con JSX, que contiene una explicación detallada de JSX.

      Guarde y cierre el archivo. Ahora, puede eliminar el archivo logo.svg, dado que era parte de la plantilla y ya no está haciendo referencia a ella:

      A continuación, actualice el estilo. Abra src/App.css:

      Sustituya el contenido por el siguiente CSS para centrar los elementos y ajustar la fuente:

      tutorial-03-component/src/App.css

      .container {
          display: flex;
          flex-direction: column;
          align-items: center;
      }
      
      button {
          font-size: 2em;
          border: 0;
          padding: 0;
          background: none;
          cursor: pointer;
      }
      
      ul {
          display: flex;
          padding: 0;
      }
      
      li {
          margin: 0 20px;
          list-style: none;
          padding: 0;
      }
      

      Se utiliza flex para centrar la <h1> principal y enumerar los elementos. También elimina estilos de botones predeterminados y de <li> para que los emojis se alineen en una fila. Puede encontrar más detalles en Cómo crear elementos de React con JSX.

      Guarde el archivo y ciérrelo.

      Abra otra ventana de terminal en el root de su proyecto. Inicie el proyecto con el siguiente comando:

      Una vez que el comando se ejecute, verá el proyecto en ejecución en su navegador web en http://localhost:3000.

      Déjelo en ejecución todo el tiempo que esté trabajando en su proyecto. Cada vez que guarde el proyecto, el navegador se actualizará de forma automática y mostrará el código más actualizado.

      Verá la página de su proyecto con Hello, World y los tres emojis que enumeró en su archivo App.js:

      Navegador con emoji

      Ahora que configuró su código, puede comenzar a unir componentes en React.

      Paso 2: Crear un componente independiente con clases de React

      Ahora que su proyecto se está ejecutando, puede comenzar a crear su componente personalizado. En este paso, creará un componente de React independiente ampliando la clase Component de React. Creará una nueva clase, agregará métodos y usará la función de representación para mostrar datos.

      Los componentes de React son elementos autónomos que puede reutilizar en una página. Al crear pequeñas piezas de código centradas, las puede mover y reutilizar a medida que su aplicación se amplía. La clave es que son autónomas y centradas, lo que le permite separar el código en piezas lógicas. De hecho, ya ha estado trabajando con componentes que se separan de forma lógica: el archivo App.js es un componente funcional, que verá en más detalle en el paso 3.

      Hay dos tipos de componentes personalizados: basados en clases y funcionales. El primer componente que va a crear es un componente basado en clases. Creará un nuevo componente denominado Instructions que explica las instrucciones del visor de emojis.

      Nota: Los componentes basados en clases solían ser la forma más popular de crear componentes de React. Pero con la introducción de los hooks de React, muchos desarrolladores y bibliotecas están comenzando a usar componentes funcionales.

      Si bien, ahora, los componentes funcionales son la norma, a menudo, encontrará componentes de clases en código heredado. No es necesario que los use, pero sí que sepa reconocerlos. También ofrecen una introducción clara a muchos conceptos futuros, como la gestión de estados. En este tutorial, aprenderá a crear componentes funcionales y de clase.

      Para comenzar, cree un archivo nuevo. Por convención, los archivos de componentes se escriben con mayúscula:

      • touch src/Instructions.js

      Luego, abra el archivo en su editor de texto:

      Primero, importe React y la clase Component y exporte Instructions con las siguientes líneas:

      tutorial-03-component/src/Instructions.js

      import React, { Component } from 'react';
      
      export default class Instructions extends Component {}
      

      La importación de React convertirá el JSX. Component es una clase de base que ampliará para crear su componente. Para ampliarlo, creó una clase que tiene el nombre de su componente (Instructions) y extendió la clase Component de base con la línea export. También está exportando esta clase como predeterminada con las palabras export default al comienzo de la declaración de clase.

      El nombre de la clase se debe escribir con mayúscula y debe coincidir con el nombre del archivo. Esto es importante cuando se utilizan herramientas de depuración, que mostrarán el nombre del componente. Si el nombre coincide con la estructura de archivos, será más fácil localizar el componente en cuestión.

      La clase Component de base tiene varios métodos que puede usar en su clase personalizada. El más importante, y el único que usará en este tutorial, es el método render(). El método render() devuelve el código JSX que desea mostrar en el navegador.

      Para comenzar, añada una pequeña explicación de la aplicación en una etiqueta <p>:

      tutorial-03-component/src/Instructions.js

      import React, { Component } from 'react';
      
      export class Instructions extends Component {
      
        render() {
          return(
            <p>Click on an emoji to view the emoji short name.</p>
          )
        }
      
      }
      

      Guarde y cierre el archivo. En este momento, todavía no hay cambios en su navegador. Esto se debe a que todavía no utilizó el componente nuevo. Para usarlo, deberá añadirlo a otro componente que se conecte con el componente root. En este proyecto, <App> es el componente root en index.js. Para que aparezca en su aplicación, deberá añadirlo al componente <App>.

      Abra src/App.js en un editor de texto:

      Primero, deberá importar el componente:

      tutorial-03-component/src/App.js

      import React from 'react';
      
      import Instructions from './Instructions';
      
      import './App.css';
      
      ...
      
      export default App;
      

      Como es la importación predeterminada, puede importar a cualquier nombre que desee. Es mejor mantener los nombres consistentes por motivos de legibilidad: la importación debe coincidir con el nombre del componente, que, a su vez, debe coincidir con el nombre del archivo. Pero la única regla fija es que el componente debe comenzar con una letra mayúscula. Así es como React sabe que se trata de un componente de React.

      Ahora que importó el componente, añádalo al resto de su código, como si fuera un elemento HTML personalizado:

      tutorial-03-component/src/App.js

      import React from 'react';
      
      import Instructions from './Instructions.js'
      
      ...
      function App() {
        const greeting = "greeting";
        const displayAction = false;
        return(
          <div className="container">
            <h1 id={greeting}>Hello, World</h1>
            {displayAction && <p>I am writing JSX</p>}
            <Instructions />
            <ul>
              {
                emojis.map(emoji => (
                  <li key={emoji.name}>
                    <button
                      onClick={displayEmojiName}
                    >
                      <span role="img" aria-label={emoji.name} id={emoji.name}>{emoji.emoji}</span>
                    </button>
                  </li>
                ))
              }
            </ul>
          </div>
        )
      }
      
      export default App;
      

      En este código, encerró el componente entre corchetes angulares. Como este componente no tiene elementos secundarios, se puede cerrar de forma automática al terminar con />.

      Guarde el archivo. Al hacerlo, la página se actualizará y verá el componente nuevo.

      Navegador con texto de instrucción

      Ahora que tiene algo de texto, puede añadir una imagen. Descargue una imagen de emoji de wikimedia y guárdela en el directorio src como emoji.svg con el siguiente comando:

      • curl -o src/emoji.svg https://upload.wikimedia.org/wikipedia/commons/3/33/Twemoji_1f602.svg

      curl realiza la solicitud a la URL y el indicador -o le permite guardar el archivo como src/emoji.svg.

      A continuación, abra el archivo de su componente:

      Importe el emoji y añádalo a su componente personalizado con un enlace dinámico:

      tutorial-03-component/src/Instructions.js

      import React, { Component } from 'react';
      import emoji from './emoji.svg'
      
      export default class Instructions extends Component {
      
        render() {
          return(
            <>
              <img alt="laughing crying emoji" src={emoji} />
              <p>Click on an emoji to view the emoji short name.</p>
            </>
          )
        }
      }
      

      Tenga en cuenta que debe incluir la extensión del archivo .svg al realizar la importación. Al importar, importa una ruta dinámica que crea webpack cuando se compila el código. Para obtener más información, consulte Cómo configurar un proyecto de React con Create React App.

      También deberá envolver las etiquetas <img> y <p> con etiquetas vacías para asegurarse de que se devuelva un único elemento.

      Guarde el archivo. Al volver a cargar el navegador, la imagen será muy grande en comparación con el resto del contenido:

      Ventana de navegador con una imagen de emoji grande

      Para reducir el tamaño de la imagen, deberá añadir algo de CSS y un className a su componente personalizado.

      Primero, en Instructions.js, cambie las etiquetas vacías por div y asigne un className de instructions:

      tutorial-03-component/src/Instructions.js

      import React, { Component } from 'react';
      import emoji from './emoji.svg'
      
      export default class Instructions extends Component {
      
        render() {
          return(
            <div className="instructions">
              <img alt="laughing crying emoji" src={emoji} />
              <p>Click on an emoji to view the emoji short name.</p>
            </div>
          )
        }
      }
      

      Guarde y cierre el archivo. A continuación, abra App.css:

      Cree reglas para el selector de clases .instructions:

      tutorial-03-component/src/App.css

      .container {
          display: flex;
          flex-direction: column;
          align-items: center;
      }
      
      ...
      
      .instructions {
          display: flex;
          flex-direction: column;
          align-items: center;
      }
      

      Al añadir un display de estilo flex, hace que img y p se centren con flexbox. Cambió la dirección para que todo se alinee verticalmente con flex-direction: column;. La línea align-items: center; centrará los elementos en la pantalla.

      Ahora que los elementos están alineados, deberá cambiar el tamaño de la imagen. Asigne a img dentro de div un valor de width (ancho) y height (alto) de 100 px.

      tutorial-03-component/src/App.css

      .container {
          display: flex;
          flex-direction: column;
          align-items: center;
      }
      
      ...
      
      .instructions {
          display: flex;
          flex-direction: column;
          align-items: center;
      }
      
      .instructions img {
          width: 100px;
          height: 100px;
      }
      

      Guarde y cierre el archivo. El navegador se volverá a cargar y verá que la imagen es mucho más pequeña:

      Ventana de navegador con una imagen más pequeña

      En este punto, creó un componente personalizado independiente y reutilizable. Para ver cómo se reutiliza, añada una segunda instancia a App.js.

      Abra App.js:

      En App.js, añada una segunda instancia del componente:

      tutorial-03-component/src/App.js

      import React from 'react';
      
      import Instructions from './Instructions.js'
      
      ...
      
      function App() {
        const greeting = "greeting";
        const displayAction = false;
        return(
          <div className="container">
            <h1 id={greeting}>Hello, World</h1>
            {displayAction && <p>I am writing JSX</p>}
            <Instructions />
            <Instructions />
            <ul>
              {
                emojis.map(emoji => (
                  <li key={emoji.name}>
                    <button
                      onClick={displayEmojiName}
                    >
                      <span role="img" aria-label={emoji.name} id={emoji.name}>{emoji.emoji}</span>
                    </button>
                  </li>
                ))
              }
            </ul>
          </div>
        )
      }
      
      export default App;
      

      Guarde el archivo. Cuando el navegador se vuelva a cargar, verá el componente dos veces.

      Navegador con dos instancias del componente Instructions

      En este caso, no desea dos instancias de Instructions, pero puede ver que el componente se puede reutilizar de forma eficiente. Es probable que use los botones o las tablas personalizados que cree varias veces en una página, por lo que son ideales para usar como componentes personalizados.

      Por ahora, puede eliminar la etiqueta de imagen adicional. En su editor de texto, elimine el segundo <Instructions /> y guarde el archivo:

      tutorial-03-component/src/App.js

      import React from 'react';
      
      import Instructions from './Instructions.js'
      
      ...
      
      function App() {
        const greeting = "greeting";
        const displayAction = false;
        return(
          <div className="container">
            <h1 id={greeting}>Hello, World</h1>
            {displayAction && <p>I am writing JSX</p>}
            <Instructions />
            <ul>
              {
                emojis.map(emoji => (
                  <li key={emoji.name}>
                    <button
                      onClick={displayEmojiName}
                    >
                      <span role="img" aria-label={emoji.name} id={emoji.name}>{emoji.emoji}</span>
                    </button>
                  </li>
                ))
              }
            </ul>
          </div>
        )
      }
      
      export default App;
      

      Ahora, tiene un componente independiente y reutilizable que puede añadir a un componente principal varias veces. La estructura que tiene ahora funciona para un número reducido de componentes, pero hay un pequeño problema: todos los archivos están mezclados. La imagen de <Instructions> se encuentra en el mismo directorio que los recursos de <App>. También se está mezclando el código CSS de <App> con el de <Instructions>.

      En el siguiente paso, creará una estructura de archivos que le dará independencia a cada componente al agrupar sus funcionalidades, estilos y dependencias, lo que le permitirá moverlos según sea necesario.

      Paso 3: Crear una estructura de archivos legible

      En este paso, creará una estructura de archivos para organizar sus componentes y recursos, como imágenes, código CSS y otros archivos de JavaScript. Agrupará el código por componentes, no por tipo de recursos. En otras palabras, no tendrá un directorio separado para CSS, imágenes y JavaScript. En su lugar, tendrá un directorio independiente para cada componente que contendrá CSS, JavaScript e imágenes pertinentes. En los dos casos, está separando cuestiones.

      Como tiene un componente independiente, necesita una estructura de archivos que agrupe el código pertinente. En este momento, todo se encuentra en el mismo directorio. Enumere los elementos en su directorio src:

      El resultado indica que todo se está desordenando bastante:

      Output

      App.css Instructions.js index.js App.js emoji.svg serviceWorker.js App.test.js index.css setupTests.js

      Tiene el código del componente <App> (App.css, App.js y App.test.js) junto a su componente root (index.css e index.js) y su componente personalizado Instructions.js.

      React es intencionalmente independiente de la estructura de archivos. No recomienda ninguna estructura en particular y el proyecto puede funcionar con diversas jerarquías de archivos. Sin embargo, recomendamos añadir cierto orden para evitar sobrecargar el directorio root con componentes, archivos CSS e imágenes que serán difíciles de navegar. Además, usar una nomenclatura explícita puede facilitar la visualización de las piezas de su proyecto que están relacionadas.  Por ejemplo, puede no ser claro que un archivo de imagen denominado Logo.svg es parte de un componente llamado Header.js.

      Una de las estructuras más simples es la creación de un directorio components con un directorio separado para cada componente. Esto le permitirá agrupar sus componentes de forma separada del código de configuración, como serviceWorker, y, a la vez, agrupar los recursos con los componentes.

      Crear un directorio components

      Para comenzar, cree un directorio denominado components:

      A continuación, pase los siguientes componentes y código al directorio: App.css, App.js, App.test.js, Instructions.js y emoji.svg:

      • mv src/App.* src/components/
      • mv src/Instructions.js src/components/
      • mv src/emoji.svg src/components/

      Aquí, está usando un carácter comodín (*) para seleccionar todos los archivos que se comienzan con App.

      Después de trasladar el código, verá un error en su terminal que ejecuta npm start.

      Output

      Failed to compile. ./src/App.js Error: ENOENT: no such file or directory, open 'your_file_path/tutorial-03-component/src/App.js'

      Recuerde que todo el código está importando usando rutas relativas. Si cambia la ruta de ciertos archivos, deberá actualizar el código.

      Para hacerlo, abra index.js.

      Luego, cambie la ruta de la importación de App para que importe desde el directorio components/.

      tutorial-03-component/src/index.js

      import React from 'react';
      import ReactDOM from 'react-dom';
      import './index.css';
      import App from './components/App';
      import * as serviceWorker from './serviceWorker';
      
      ...
      
      serviceWorker.unregister();
      

      Guarde y cierre el archivo. Su secuencia de comandos detectará los cambios, y el error desaparecerá.

      Ahora, tiene componentes en un directorio separado. A medida que sus aplicaciones se vuelven más complejas, es posible que tenga directorios para servicios API, almacenes de datos y funciones de utilidad. Separar el código de los componentes es el primer paso, pero todavía tiene el código CSS de Instructions mezclado en el archivo App.css.  Para crear esta separación lógica, primero, trasladará los componentes a directorios separados.

      Trasladar componentes a directorios individuales

      Primero, cree un directorio específicamente para el componente <App>:

      Luego, traslade los archivos relacionados al directorio nuevo:

      • mv src/components/App.* src/components/App

      Al hacerlo, obtendrá un error similar al de la última sección:

      Output

      Failed to compile. ./src/components/App.js Error: ENOENT: no such file or directory, open 'your_file_path/tutorial-03-component/src/components/App.js'

      En este caso, deberá actualizar dos cosas. Primero, deberá actualizar la ruta en index.js.

      Abra el archivo index.js:

      Luego, actualice la ruta de importación de App para que apunte al componente App en el directorio App.

      tutorial-03-component/src/index.js

      import React from 'react';
      import ReactDOM from 'react-dom';
      import './index.css';
      import App from './components/App/App';
      import * as serviceWorker from './serviceWorker';
      
      ...
      
      serviceWorker.unregister();
      

      Guarde y cierre el archivo. La aplicación seguirá sin ejecutarse. Verá un error como este:

      Output

      Failed to compile. ./src/components/App/App.js Module not found: Can't resolve './Instructions.js' in 'your_file_path/tutorial-03-component/src/components/App'

      Como <Instructions> no se encuentra en el mismo nivel de directorio que el componente <App>, deberá cambiar la ruta de importación. Antes de hacerlo, cree un directorio para Instructions. Cree un directorio denominado Instructions en el directorio src/components:

      • mkdir src/components/Instructions

      Luego, traslade Instructions.js y emoji.svg a ese directorio:

      • mv src/components/Instructions.js src/components/Instructions
      • mv src/components/emoji.svg src/components/Instructions

      Ahora que se creó el directorio del componente Instructions, puede terminar de actualizar las rutas de archivos para conectar su componente a su aplicación.

      Actualizar rutas de importación

      Ahora que los componentes se encuentran en directorios individuales, puede ajustar la ruta de importación en App.js.

      Abra App.js:

      • nano src/components/App/App.js

      Como la ruta es relativa, deberá subir un directorio, src/components, y, luego, ingresar al directorio Instructions de Instructions.js, pero, como se trata de un archivo JavaScript, no necesita la importación final.

      tutorial-03-component/src/components/App/App.js

      import React from 'react';
      
      import Instructions from '../Instructions/Instructions.js';
      
      import './App.css';
      
      ...
      
      export default App;
      

      Guarde y cierre el archivo. Ahora que todas sus importaciones están usando la ruta correcta, su navegador se actualizará y mostrará la aplicación.

      Ventana de navegador con una imagen más pequeña

      Nota: También puede denominar el archivo root de cada directorio index.js. Por ejemplo, en lugar de src/components/App/App.js, podría crear src/components/App/index.js. La ventaja es que, de esta manera, sus importaciones son ligeramente más pequeñas. Si la ruta apunta a un directorio, la importación buscará un archivo index.js. La importación de src/components/App/index.js en el archivo src/index.js sería import ./components/App. La desventaja de este método es que termina teniendo muchos archivos con el mismo nombre, lo que puede dificultar la lectura en algunos editores de texto. En última instancia, se trata de una decisión personal y de equipo, pero lo mejor es ser coherente.

      Separar código en archivos compartidos

      Ahora, cada componente tiene su propio directorio, pero no todo es completamente independiente. El último paso es extraer el CSS de Instructions a un archivo separado.

      Primero, cree un archivo CSS en src/components/Instructions:

      • touch src/components/Instructions/Instructions.css

      Luego, abra el archivo CSS en su editor de texto:

      • nano src/components/Instructions/Instructions.css

      Añada el CSS de instrucciones que creó en una sección anterior:

      tutorial-03-component/src/components/Instructions/Instructions.css

      .instructions {
          display: flex;
          flex-direction: column;
          align-items: center;
      }
      
      .instructions img {
          width: 100px;
          height: 100px;
      }
      

      Guarde y cierre el archivo. Luego, elimine el CSS de instrucciones de src/components/App/App.css.

      • nano src/components/App/App.css

      Elimine las líneas sobre .instructions. El archivo final tendrá el siguiente aspecto:

      tutorial-03-component/src/components/App/App.css

      .container {
          display: flex;
          flex-direction: column;
          align-items: center;
      }
      
      button {
          font-size: 2em;
          border: 0;
          padding: 0;
          background: none;
          cursor: pointer;
      }
      
      ul {
          display: flex;
          padding: 0;
      }
      
      li {
          margin: 0 20px;
          list-style: none;
          padding: 0;
      }
      

      Guarde y cierre el archivo. Por último, importe el CSS en Instructions.js:

      • nano src/components/Instructions/Instructions.js

      Importe el CSS usando la ruta relativa:

      tutorial-03-component/src/components/Instructions/Instructions.js

      import React, { Component } from 'react';
      import './Instructions.css';
      import emoji from './emoji.svg'
      
      export default class Instructions extends Component {
      
        render() {
          return(
            <div className="instructions">
              <img alt="laughing crying emoji" src={emoji} />
              <p>Click on an emoji to view the emoji short name.</p>
            </div>
          )
        }
      }
      

      Guarde y cierre el archivo. La ventana de su navegador se verá igual que antes, excepto que, ahora, todos los recursos del archivo están agrupados en el mismo directorio.

      Ventana de navegador con una imagen más pequeña

      Ahora, observe la estructura una última vez. Primero, el directorio src/:

      Cuenta con el componente root index.js y el CSS relacionado index.css junto al directorio components/ y archivos de utilidad, como serviceWorker.js y setupTests.js:

      Output

      components serviceWorker.js index.css setupTests.js index.js

      Luego, observe el interior de components:

      Verá un directorio para cada componente:

      Output

      App Instructions

      Si observa el interior de cada componente, verá el código del componente y archivos CSS, de prueba y de imagen, si existen.

      Output

      App.css App.js App.test.js
      • ls src/components/Instructions

      Output

      Instructions.css Instructions.js emoji.svg

      En este punto, creó una estructura sólida para su proyecto. Movió mucho código, pero, ahora que tiene una estructura, se ampliará más fácilmente.

      Esta no es la única manera de crear su estructura. Algunas estructuras de archivos pueden aprovechar la división de código al especificar un directorio que se divida en varios paquetes. Otras estructuras de archivos se dividen por ruta y utilizan un directorio común para los componentes que se utilizan en todas las rutas.

      Por ahora, continúe con un enfoque menos complejo. Si surge la necesidad de tener otra estructura, siempre es más fácil pasar de lo sencillo a lo complejo. Comenzar con una estructura compleja antes de necesitarla, dificultará la refactorización.

      Ahora que creó y organizó un componente basado en clases, en el siguiente paso, creará un componente funcional.

      Paso 4: Crear un componente funcional

      En este paso, creará un componente funcional. Los componentes funcionales son los más comunes en el código de React contemporáneo. Estos componentes suelen ser más cortos y, a diferencia de los componentes basados en clases, pueden usar hooks de React, una nueva manera de gestionar estados y eventos.

      Un componente funcional es una función de JavaScript que devuelve cierto JSX. No requiere ampliar nada y no hay métodos especiales que memorizar.

      Para refactorizar <Instructions> como componente funcional, deberá cambiar la clase a una función y eliminar el método de representación de modo que solo quede la instrucción de retorno.

      Para hacerlo, primero, abra Instructions.js en un editor de texto.

      • nano src/components/Instructions/Instructions.js

      Cambie la declaración class a una declaración function:

      tutorial-03-component/src/components/Instructions/Instructions.js

      import React, { Component } from 'react';
      import './Instructions.css';
      import emoji from './emoji.svg'
      
      export default function Instructions() {
        render() {
          return(
            <div className="instructions">
              <img alt="laughing crying emoji" src={emoji} />
              <p>Click on an emoji to view the emoji short name.</p>
            </div>
          )
        }
      }
      

      Luego, elimine la importación de { Component }:

      tutorial-03-component/src/components/Instructions/Instructions.js

      import React from 'react';
      import './Instructions.css';
      import emoji from './emoji.svg'
      
      export default function Instructions() {
      
        render() {
          return(
            <div className="instructions">
              <img alt="laughing crying emoji" src={emoji} />
              <p>Click on an emoji to view the emoji short name.</p>
            </div>
          )
        }
      }
      

      Por último, elimine el método render(). En este punto, solo está devolviendo JSX.

      tutorial-03-component/src/components/Instructions/Instructions.js

      import React from 'react';
      import './Instructions.css';
      import emoji from './emoji.svg'
      
      export default function Instructions() {
        return(
          <div className="instructions">
              <img alt="laughing crying emoji" src={emoji} />
              <p>Click on an emoji to view the emoji short name.</p>
          </div>
        )
      }
      

      Guarde el archivo. El navegador se actualizará y verá su página como antes.

      Navegador con emoji

      También puede volver a escribir la función como función de flecha usando la devolución implícita. La principal diferencia es que pierde el cuerpo de la función. También deberá, primero, asignar la función a una variable y, luego, exportar esa variable:

      tutorial-03-component/src/components/Instructions/Instructions.js

      import React from 'react';
      import './Instructions.css';
      import emoji from './emoji.svg'
      
      const Instructions = () => (
        <div className="instructions">
          <img alt="laughing crying emoji" src={emoji} />
          <p>Click on an emoji to view the emoji short name.</p>
        </div>
      )
      
      export default Instructions;
      

      Los componentes funcionales simples y los componentes basados en clases son muy similares. Cuando tiene un componente sencillo que no almacena estados, lo mejor es usar un componente funcional. La diferencia real entre los dos tipos de componentes es la forma en que se almacenan su estado y sus propiedades de uso. Los componentes basados en clases utilizan métodos y propiedades para establecer el estado y suelen ser algo más largos. Los componentes funcionales utilizan hooks para almacenar el estado o gestionar cambios y suelen ser un poco más cortos.

      Conclusión

      Ahora, tiene una aplicación pequeña con piezas independientes. Creó componentes de los dos tipos principales: funcionales y basados en clases. Separó las partes de los componentes en directorios para poder mantener agrupadas porciones de código similares. También importó y reutilizó los componentes.

      Si comprende los componentes, puede comenzar a ver sus aplicaciones como piezas que puede desarmar y volver a armar. Los proyectos se vuelven modulares e intercambiables. La capacidad de ver aplicaciones completas como series de componentes es un paso importante para comprender React. Si desea acceder a más tutoriales de React, consulte nuestra página temática de React o regrese a la página de la serie Cómo programar con React.js.



      Source link

      Criando erros personalizados em Go


      Introdução

      O Go oferece dois métodos para criar erros na biblioteca padrão, errors.New e fmt.Errorf. Ao comunicar informações de erros mais complicados aos seus usuários, ou para si mesmo – num momento futuro, por vezes esses dois mecanismos não são o suficiente para capturar e relatar adequadamente o que aconteceu. Para transmitir essa informação de erro mais complexo e obter mais funcionalidade, podemos implementar o tipo de interface da biblioteca padrão, error.

      A sintaxe para isso seria a seguinte:

      type error interface {
        Error() string
      }
      

      O pacote builtin define error como uma interface com um método único Error() que retorna uma mensagem de erro como uma string. Ao implementar esse método, podemos transformar qualquer tipo que definirmos em um erro nosso.

      Vamos tentar executar o exemplo a seguir para ver uma implementação da interface error:

      package main
      
      import (
          "fmt"
          "os"
      )
      
      type MyError struct{}
      
      func (m *MyError) Error() string {
          return "boom"
      }
      
      func sayHello() (string, error) {
          return "", &MyError{}
      }
      
      func main() {
          s, err := sayHello()
          if err != nil {
              fmt.Println("unexpected error: err:", err)
              os.Exit(1)
          }
          fmt.Println("The string:", s)
      }
      

      Veremos o seguinte resultado:

      Output

      unexpected error: err: boom exit status 1

      Aqui, criamos um novo tipo de struct vazio, MyError e definimos o método Error() nele. O método Error() retorna a string "boom".

      Dentro do main(), chamamos a função sayHello que retorna uma string vazia e uma nova instância do MyError. Como o sayHello​​​ sempre retornará um erro, a chamada fmt.Println dentro do corpo da instrução if no main() sempre irá ser executada. Então, usamos o fmt.Println para imprimir a curta string do prefixo "unexpected error:" junto com a instância do MyError mantida dentro da variável err.

      Note que não precisamos chamar diretamente o Error(), uma vez que o pacote fmt consegue detectar automaticamente que se trata de uma implementação de error. Ele chama o Error() de maneira transparente para obter a string "boom“ e a concatena com a string do prefixo "unexpected error: err:".

      Coletando informações detalhadas em um erro personalizado

      Às vezes, um erro personalizado é a maneira mais limpa de capturar informações detalhadas de erro. Por exemplo, vamos supor que queremos capturar o código de status de erros produzidos por um pedido do HTTP; execute o programa a seguir para ver uma implementação do error que nos permite capturar de modo correto tais informações:

      package main
      
      import (
          "errors"
          "fmt"
          "os"
      )
      
      type RequestError struct {
          StatusCode int
      
          Err error
      }
      
      func (r *RequestError) Error() string {
          return fmt.Sprintf("status %d: err %v", r.StatusCode, r.Err)
      }
      
      func doRequest() error {
          return &RequestError{
              StatusCode: 503,
              Err:        errors.New("unavailable"),
          }
      }
      
      func main() {
          err := doRequest()
          if err != nil {
              fmt.Println(err)
              os.Exit(1)
          }
          fmt.Println("success!")
      }
      

      Veremos o seguinte resultado:

      Output

      status 503: err unavailable exit status 1

      Neste exemplo, criamos uma nova instância do RequestError e fornecemos o código de status e um erro usando a função errors.New da biblioteca padrão. Então, imprimimos isso usando o fmt.Println como em exemplos anteriores.

      Dentro do método Error() do RequestError, usamos a função fmt.Sprintf para construir uma string usando as informações fornecidas quando o erro foi criado.

      Declarações de tipo e erros personalizados

      A interface error mostra somente um método, mas podemos precisar acessar os outros métodos de implementações de error para lidar com um erro de maneira correta. Por exemplo, podemos ter várias implementações personalizadas do error que são temporárias e podem ser repetidas—indicadas pela presença de um método Temporary().

      As interfaces fornecem uma visualização limitada do conjunto mais amplo de métodos fornecidos por tipos. Assim, devemos usar uma asserção de tipo para alterar os métodos que a visualização está exibindo ou removê-la totalmente.

      O exemplo a seguir amplia o RequestError mostrado anteriormente com um método Temporary() que indicará se os chamadores devem ou não realizar o pedido novamente:

      package main
      
      import (
          "errors"
          "fmt"
          "net/http"
          "os"
      )
      
      type RequestError struct {
          StatusCode int
      
          Err error
      }
      
      func (r *RequestError) Error() string {
          return r.Err.Error()
      }
      
      func (r *RequestError) Temporary() bool {
          return r.StatusCode == http.StatusServiceUnavailable // 503
      }
      
      func doRequest() error {
          return &RequestError{
              StatusCode: 503,
              Err:        errors.New("unavailable"),
          }
      }
      
      func main() {
          err := doRequest()
          if err != nil {
              fmt.Println(err)
              re, ok := err.(*RequestError)
              if ok {
                  if re.Temporary() {
                      fmt.Println("This request can be tried again")
                  } else {
                      fmt.Println("This request cannot be tried again")
                  }
              }
              os.Exit(1)
          }
      
          fmt.Println("success!")
      }
      

      Veremos o seguinte resultado:

      Output

      unavailable This request can be tried again exit status 1

      Dentro do main(), chamamos o doRequest() que retorna uma interface error para nós. Primeiro, imprimimos a mensagem de erro que o método Error() retornou. Em seguida, tentamos expor todos os métodos do RequestError usando a asserção de tipo re, ok := err.(​​ *RequestError)​​​. Se o tipo declarado foi bem sucedido, usamos o método Temporary() para ver se este erro é um erro temporário. Como o StatusCode definido pelo doRequest() é o 503, que corresponde ao http.StatusServiceUnavailable, ele retorna true e faz com que a mensagem "This request can be tried again" (Esta solicitação pode ser repetida) seja impressa. Na prática, faríamos outro pedido em vez de imprimir uma mensagem.

      Erros de empacotamento

      Normalmente, um erro será gerado a partir de algo fora do seu programa, como: um banco de dados, uma conexão de rede etc. As mensagens de erro fornecidas a partir desses erros não ajudam ninguém a encontrar a origem do erro. O uso de erros de empacotamento com informações extra no início de uma mensagem de erro forneceria o contexto necessário para uma depuração bem-sucedida.

      O exemplo a seguir demonstra como podemos anexar informações contextuais a um error – que, de outro modo, seria criptografado – retornado de alguma outra função:

      package main
      
      import (
          "errors"
          "fmt"
      )
      
      type WrappedError struct {
          Context string
          Err     error
      }
      
      func (w *WrappedError) Error() string {
          return fmt.Sprintf("%s: %v", w.Context, w.Err)
      }
      
      func Wrap(err error, info string) *WrappedError {
          return &WrappedError{
              Context: info,
              Err:     err,
          }
      }
      
      func main() {
          err := errors.New("boom!")
          err = Wrap(err, "main")
      
          fmt.Println(err)
      }
      

      Veremos o seguinte resultado:

      Output

      main: boom!

      WrappedError é uma struct com dois campos: uma mensagem de contexto em forma de string e um error sobre o qual o WrappedError fornece mais informações. Quando o método Error() for chamado, usaremos o fmt.Sprintf novamente para imprimir a mensagem de contexto e, em seguida, o error (fmt.Sprintf sabe chamar implicitamente o método Error() também).

      Dentro do main(), criamos um erro usando o errors.New e, em seguida, empacotamos aquele erro usando a função Wrap que definimos. Isso nos permite indicar que esse error foi gerado no "main". Além disso, já que o nosso WrappedError também é um error, podemos empacotar outros WrappedErrors — isso nos permitiria ver uma cadeia que que nos ajudaria a rastrear a fonte do erro. Com um pouco de ajuda da biblioteca padrão, podemos até incorporar traços de pilha completos em nossos erros.

      Conclusão

      Como a interface error é apenas um método único, vimos que temos grande flexibilidade na oferta de diferentes tipos de erros para situações diferentes. Isso pode abranger tudo – desde a comunicação de vários fragmentos de informação como parte de um erro até a implementação de uma retirada exponencial. Embora os mecanismos de gerenciamento de erros no Go, em princípio, possam parecer simplistas, podemos chegar a um gerenciamento bastante detalhado usando esses erros personalizados para lidar com situações comuns e incomuns.

      O Go tem outro mecanismo para comunicar comportamentos inesperados, o panics (pânicos). No nosso próximo artigo na série de tratamento de erros, vamos examinar o panics — o que são e como lidar com eles.



      Source link

      Crear errores personalizados en Go


      Introducción

      Go ofrece dos métodos para crear errores en la biblioteca estándar: errors.New y fmt.Errorf. Cuando comunica información de error más complicada a sus usuarios, o a usted mismo al realizar una depuración, a veces estos dos mecanismos no son suficientes para capturar e informar de manera adecuada lo que sucedió. Para expresar esta información de error más compleja, y ampliar la funcionalidad, podemos implementar el tipo de interfaz de biblioteca estándar: error.

      La sintaxis para esto sería la siguiente:

      type error interface {
        Error() string
      }
      

      El paquete builtin define error como una interfaz con un único método Error() que muestra un mensaje de error como una cadena. Al implementar este método, podemos transformar cualquier tipo que definamos en un error propio.

      Intentaremos ejecutar el siguiente ejemplo para ver una implementación de la interfaz error:

      package main
      
      import (
          "fmt"
          "os"
      )
      
      type MyError struct{}
      
      func (m *MyError) Error() string {
          return "boom"
      }
      
      func sayHello() (string, error) {
          return "", &MyError{}
      }
      
      func main() {
          s, err := sayHello()
          if err != nil {
              fmt.Println("unexpected error: err:", err)
              os.Exit(1)
          }
          fmt.Println("The string:", s)
      }
      

      Veremos el siguiente resultado:

      Output

      unexpected error: err: boom exit status 1

      Aquí, hemos creado un nuevo tipo de estructura vacía, MyError, y hemos definido el método Error() en ella. El método Error() muestra la cadena "boom".

      En main(), invocamos la función sayHello que muestra una cadena vacía y una nueva instancia de MyError. Ya que sayHello siempre mostrará un error, la invocación fmt.PrintIn dentro del cuerpo de la declaración if en main() siempre se ejecutará. Utilizaremos fmt.PrintIn para imprimir la cadena de prefijo corto "unexpected error:" junto con la instancia de MyError que está en la variable err.

      Observe que no tenemos que invocar directamente Error(), ya que el paquete fmt puede detectar automáticamente que esta es una implementación de error. Invoca Error() de forma transparente para obtener la cadena "boom" y la concatena con la cadena de prefijo "unexpected error: err:".

      Recopilar información detallada en un error personalizado

      A veces, un error personalizado es la alternativa más prolija para capturar información detallada de un error. Por ejemplo, supongamos que queremos capturar el código de estado de los errores producidos por una solicitud HTTP. Ejecute el siguiente programa para ver una implementación de error que nos permita capturar de forma prolija esa información:

      package main
      
      import (
          "errors"
          "fmt"
          "os"
      )
      
      type RequestError struct {
          StatusCode int
      
          Err error
      }
      
      func (r *RequestError) Error() string {
          return fmt.Sprintf("status %d: err %v", r.StatusCode, r.Err)
      }
      
      func doRequest() error {
          return &RequestError{
              StatusCode: 503,
              Err:        errors.New("unavailable"),
          }
      }
      
      func main() {
          err := doRequest()
          if err != nil {
              fmt.Println(err)
              os.Exit(1)
          }
          fmt.Println("success!")
      }
      

      Verá el siguiente resultado:

      Output

      status 503: err unavailable exit status 1

      En este ejemplo, creamos una nueva instancia de RequestError y proporcionamos el código de estado y un error usando la función errors.New de la biblioteca estándar. A continuación, imprimimos esto usando fmt.PrintIn como en los ejemplos anteriores.

      En el método Error() de RequestError, usamos la función fmt.Sprintf para construir una cadena empleando la información proporcionada cuando se creó el error.

      Aserciones de tipo y errores personalizados

      La interfaz error expone solo un método, pero es posible que necesitemos acceder a otros métodos de implementaciones de error para gestionar un error de forma correcta. Por ejemplo, es posible que tengamos varias implementaciones personalizadas de error que sean temporales y puedan probarse nuevamente, denotadas por la presencia de un método Temporary().

      Las interfaces proporcionan una vista reducida del conjunto, más amplio, de métodos proporcionados por los tipos, de modo que debemos usar una_ aserción de tipo_ para cambiar los métodos que la vista muestra o para eliminarla completamente.

      El siguiente ejemplo aumenta el RequestError previamente mostrado para tener un método Temporary() que indicará si quienes realizan la invocación deberian intentar realizar nuevamente la solicitud o no:

      package main
      
      import (
          "errors"
          "fmt"
          "net/http"
          "os"
      )
      
      type RequestError struct {
          StatusCode int
      
          Err error
      }
      
      func (r *RequestError) Error() string {
          return r.Err.Error()
      }
      
      func (r *RequestError) Temporary() bool {
          return r.StatusCode == http.StatusServiceUnavailable // 503
      }
      
      func doRequest() error {
          return &RequestError{
              StatusCode: 503,
              Err:        errors.New("unavailable"),
          }
      }
      
      func main() {
          err := doRequest()
          if err != nil {
              fmt.Println(err)
              re, ok := err.(*RequestError)
              if ok {
                  if re.Temporary() {
                      fmt.Println("This request can be tried again")
                  } else {
                      fmt.Println("This request cannot be tried again")
                  }
              }
              os.Exit(1)
          }
      
          fmt.Println("success!")
      }
      

      Verá el siguiente resultado:

      Output

      unavailable This request can be tried again exit status 1

      En main(), invocamos doRequest() que muestra una interfaz error. Primero, imprimimos el mensaje de error mostrado por el método Error(). A continuación, intentamos exponer todos los métodos de RequestError usando la afirmación de tipo re, ok := err.( *RequestError). Si la aserción de tipo se realizó correctamente, usaremos el método Temporary() para ver si este error es temporal. Debido a que el StatusCode establecido por doRequest() es 503, que coincide con http.StatusServiceUnavailable, con esto se muestra true y se imprime "This request can be tried again". En la práctica, en vez de eso, realizaríamos otra solicitud en vez de imprimir un mensaje.

      Ajustar errores

      Comúnmente, un error se generará a partir de algo externo a su programa, como una base de datos y una conexión de red, entre otros ejemplos. Los mensajes de error proporcionados a partir de estos errores no ayudan a encontrar el origen del error. Ajustar los errores con información adicional al principio de un mensaje de error proporcionará el contexto necesario para realizar correctamente la depuración.

      En el siguiente ejemplo, se demuestra la forma en que podemos añadir información contextual a un error, mostrado por alguna otra función, que de otra forma sería enigmático.

      package main
      
      import (
          "errors"
          "fmt"
      )
      
      type WrappedError struct {
          Context string
          Err     error
      }
      
      func (w *WrappedError) Error() string {
          return fmt.Sprintf("%s: %v", w.Context, w.Err)
      }
      
      func Wrap(err error, info string) *WrappedError {
          return &WrappedError{
              Context: info,
              Err:     err,
          }
      }
      
      func main() {
          err := errors.New("boom!")
          err = Wrap(err, "main")
      
          fmt.Println(err)
      }
      

      Verá el siguiente resultado:

      Output

      main: boom!

      WrappedError es una estructura con dos campos: un mensaje de contexto como una string y un error sobre el cual WrappedError proporciona más información. Cuando se invoca el método Error(), de nuevo usamos fmt.Sprintf para imprimir el mensaje de contexto; luego el error (fmt.Sprintf sabe cómo invocar de forma implícita el método Error() también).

      En main(), creamos un error usando errors.New y luego ajustamos ese error usando la función Wrap que definimos. Esto nos permite indicar que este error se generó en "main". Además, ya que nuestro WrappedError es también un error, podríamos ajustar otro WrappedError; esto nos permitiría ver una cadena para poder rastrear el origen del error. Con algo de ayuda de la biblioteca estándar, podemos incluso integrar seguimientos de pila completos en nuestros errores.

      Conclusión

      Debido a que la interfaz error es solo un método único, hemos visto que disponemos de una gran flexibilidad para proporcionar diferentes tipos de error para diferentes situaciones. Esto puede abarcar todo, desde la comunicación de varios fragmentos de información como parte de un error hasta la implementación de un retroceso exponencial. Aunque los mecanismos de gestión de errores en Go pueden parecer, a primera vista, simplistas, podemos lograr un manejo bastante bueno usando estos errores personalizados para gestionar situaciones comunes y atípicas.

      Go tiene otro mecanismo para comunicar el comportamiento inesperado: los panics. En nuestro siguiente artículo de la serie de gestión de errores, veremos los panics, qué son y la forma gestionarlos.



      Source link