Todos los conceptos fundamentales de React.js, atascados en este único artículo mediano

El año pasado escribí un libro corto sobre el aprendizaje de React.js que resultó ser de aproximadamente 100 páginas. Este año, voy a desafiarme a mí mismo para resumirlo como un artículo sobre Medium.

Este artículo no cubrirá lo que es React o por qué debería aprenderlo . En cambio, esta es una introducción práctica a los fundamentos de React.js para aquellos que ya están familiarizados con JavaScript y conocen los conceptos básicos de DOM API .

Todos los ejemplos de código a continuación están etiquetados para referencia. Están destinados puramente a proporcionar ejemplos de conceptos. La mayoría de ellos se pueden escribir de una manera mucho mejor.

Fundamental n. ° 1: React se trata de componentes

React está diseñado en torno al concepto de componentes reutilizables. Usted define componentes pequeños y los junta para formar componentes más grandes.

Todos los componentes pequeños o grandes son reutilizables, incluso en diferentes proyectos.

Un componente de React, en su forma más simple, es una función de JavaScript antigua:

 // Ejemplo 1 
 // https://jscomplete.com/repl?j=Sy3QAdKHW
 botón de función (accesorios) { 
 // Devuelve un elemento DOM aquí. Por ejemplo: 
 return <button type = "submit"> {props.label} </ button>; 
 }
 // Para renderizar el componente Button en el navegador 
 ReactDOM.render (<Button label = "Guardar" />, mountNode)

Las llaves usadas para la etiqueta del botón se explican a continuación. No te preocupes por ellos ahora. ReactDOM también se explicará más adelante, pero si desea probar este ejemplo y todos los próximos ejemplos de código, la función de render anterior es lo que necesita.

El segundo argumento para ReactDOM.render es el elemento DOM de destino que React tomará y controlará. En el jsComplete React Playground , puedes usar la variable especial mountNode .

JavaScript REPL y Playground para React.js
Pruebe JavaScript moderno y el código React.js en el navegador sin ninguna configuración jscomplete.com/react

Tenga en cuenta lo siguiente sobre el Ejemplo 1:

  • El nombre del componente comienza con una letra mayúscula. Esto es necesario ya que trataremos con una combinación de elementos HTML y elementos React. Los nombres en minúsculas están reservados para elementos HTML. De hecho, siga adelante y trate de nombrar el componente React simplemente como “botón” y vea cómo ReactDOM ignorará la función y renderizará un botón HTML vacío normal.
  • Cada componente recibe una lista de atributos, al igual que los elementos HTML. En React, esta lista se llama accesorios . Con un componente de función, puedes nombrarlo sin embargo.
  • Escribimos de forma extraña lo que parece HTML en la salida devuelta del componente de función Button anterior. Esto no es ni JavaScript ni HTML, y ni siquiera es React.js. Pero es tan popular que se convirtió en el predeterminado en las aplicaciones Reaccionar. Se llama JSX y es una extensión de JavaScript. ¡JSX es también un compromiso ! Continúe e intente devolver cualquier otro elemento HTML dentro de la función anterior y vea cómo son compatibles (por ejemplo, devolver un elemento de entrada de texto).

Fundamental # 2: ¿Qué flujo es JSX?

El ejemplo 1 anterior se puede escribir en React.js puro sin JSX de la siguiente manera:

 // Ejemplo 2 - Componente Reaccionar sin JSX 
 // https://jscomplete.com/repl?j=HyiEwoYB-
 botón de función (accesorios) { 
 devolver React.createElement ( 
 "botón", 
 {tipo: "enviar"}, 
 props.label 
 ); 
 }
 // Para usar Button, harías algo como 
 ReactDOM.render ( 
 React.createElement (Button, {label: "Guardar"}), 
 mountNode 
 );

La función createElement es la función principal en la API de nivel superior de React. Es 1 de un total de 8 cosas en ese nivel que debes aprender. Así de pequeña es la API React.

Al igual que DOM tiene una función document.createElement para crear un elemento especificado por un nombre de etiqueta, la función createElement de React es una función de nivel superior que puede hacer lo document.createElement hace document.createElement , pero también se puede usar para crear un elemento para representar un componente Reaccionar Hicimos esto último cuando usamos el componente Button en el Ejemplo 2 anterior.

A diferencia de document.createElement , de React createElement acepta una serie dinámica de argumentos a partir del segundo para representar a los hijos del elemento creado. Entonces createElement realmente crea un árbol .

Aquí hay un ejemplo de eso:

 // Ejemplo 3 - API createElement de React 
 // https://jscomplete.com/repl?j=r1GNoiFBb
 const InputForm = React.createElement ( 
 "formar", 
 {target: "_blank", acción: " https://google.com/search "}, 
 React.createElement ("div", null, "Ingresar entrada y hacer clic en Buscar"), 
 React.createElement ("input", {name: "q", className: "input"}), 
 React.createElement (Button, {label: "Search"}) 
 );
 // InputForm usa el componente Button, por lo que necesitamos eso también: 
 botón de función (accesorios) { 
 devolver React.createElement ( 
 "botón", 
 {tipo: "enviar"}, 
 props.label 
 ); 
 }
 // Entonces podemos usar InputForm directamente con .render 
 ReactDOM.render (InputForm, mountNode);

Tenga en cuenta algunas cosas sobre el ejemplo anterior:

  • InputForm no es un componente Reaccionar; es solo un elemento React. Es por eso que lo usamos directamente en la llamada ReactDOM.render y no con <InputForm /> .
  • La función React.createElement aceptó varios argumentos después de los dos primeros. Su lista de argumentos a partir de la tercera comprende la lista de elementos secundarios para el elemento creado.
  • Pudimos anidar React.createElement llamadas a React.createElement porque todo es JavaScript.
  • El segundo argumento para React.createElement puede ser nulo o un objeto vacío cuando no se necesitan atributos o utilería para el elemento.
  • Podemos mezclar elementos HTML con elementos React.
  • La API de React intenta estar lo más cerca posible de la DOM API, por eso utilizamos className lugar de class para el elemento de entrada. En secreto, todos deseamos que la API de React se convierta en parte de la API de DOM en sí. Porque, ya sabes, es mucho mejor.

El código anterior es lo que entiende el navegador cuando incluye la biblioteca React. El navegador no se ocupa de ningún negocio JSX. Sin embargo, a los seres humanos nos gusta ver y trabajar con HTML en lugar de estas llamadas a createElement (imagina construir un sitio web con solo document.createElement , ¡lo cual puedes!). Esta es la razón por la cual existe el compromiso JSX. En lugar de escribir el formulario anterior con llamadas a React.createElement , podemos escribirlo con una sintaxis muy similar a HTML:

 // Ejemplo 4 - JSX (compáralo con el Ejemplo 3) 
 // https://jscomplete.com/repl?j=SJWy3otHW
 const InputForm = 
 <formulario target = "_ blank" action = " https://google.com/search "> 
 <div> Ingrese la entrada y haga clic en Buscar </ div> 
 <input name = "q" className = "input" /> 
 <Etiqueta de botón = "Buscar" /> 
 </ form>;
 // InputForm "still" usa el componente Button, por lo que también necesitamos eso. 
 // O JSX o la forma normal harían 
 botón de función (accesorios) { 
 // Devuelve un elemento DOM aquí. Por ejemplo: 
 return <button type = "submit"> {props.label} </ button>; 
 }
 // Entonces podemos usar InputForm directamente con .render 
 ReactDOM.render (InputForm, mountNode);

Tenga en cuenta algunas cosas sobre lo anterior:

  • No es HTML Por ejemplo, todavía estamos haciendo className lugar de class .
  • Todavía estamos considerando lo que parece HTML arriba como JavaScript. Vea cómo agregué un punto y coma al final.

Lo que escribimos arriba (Ejemplo 4) es JSX. Sin embargo, lo que llevamos al navegador es la versión compilada del mismo (Ejemplo 3). Para que esto suceda, debemos usar un preprocesador para convertir la versión JSX en la versión React.createElement .

Eso es JSX. Es un compromiso que nos permite escribir nuestros componentes React en una sintaxis similar a HTML, que es un buen trato.

La palabra “Flujo” en el encabezado anterior fue elegida para rimar, pero también es el nombre de una popular arquitectura de aplicaciones popularizada por Facebook. La implementación más famosa de las cuales es Redux. Flux se adapta perfectamente al patrón reactivo de React.

JSX, por cierto, se puede usar solo. No es una cosa solo de Reacción.

Fundamental # 3: Puedes usar expresiones de JavaScript en cualquier parte de JSX

Dentro de una sección JSX, puede usar cualquier expresión de JavaScript dentro de un par de llaves.

 // Ejemplo 5 - Uso de expresiones de JavaScript en JSX 
 // https://jscomplete.com/repl?j=SkNN3oYSW
 const RandomValue = () => 
 <div> 
 {Math.floor (Math.random () * 100)} 
 </ div>;
 // Para usarlo: 
 ReactDOM.render (<RandomValue />, mountNode);

Cualquier expresión de JavaScript puede ir dentro de esas llaves. Esto es equivalente a la sintaxis de interpolación ${} en los literales de plantilla de JavaScript.

Esta es la única restricción dentro de JSX: solo expresiones. Entonces, por ejemplo, no puede usar una instrucción if regular, pero una expresión ternaria está bien.

Las variables de JavaScript también son expresiones, por lo que cuando el componente recibe una lista de accesorios (el componente RandomValue no, los props son opcionales), puede usar estos accesorios dentro de las llaves. Hicimos esto en el componente Button arriba (Ejemplo 1).

Los objetos JavaScript también son expresiones. A veces utilizamos un objeto JavaScript dentro de las llaves, lo que lo hace parecer llaves dobles, pero en realidad es solo un objeto dentro de las llaves. Un caso de uso de eso es pasar un objeto de estilo CSS al atributo de style especial en React:

 // Ejemplo 6 - Un objeto pasado al accesorio especial React style 
 // https://jscomplete.com/repl?j=S1Kw2sFHb
 const ErrorDisplay = ({mensaje}) => 
 <div style = {{color: 'red', backgroundColor: 'yellow'}}> 
 {mensaje} 
 </ div>;
 // Úselo: 
 ReactDOM.render ( 
 <ErrorDisplay 
 message = "Estos no son los droides que estás buscando" 
 />, 
 mountNode 
 );

Tenga en cuenta cómo desestructurado solo el mensaje fuera del argumento de utilería. También tenga en cuenta cómo el atributo de style anterior es especial (de nuevo, no es HTML, está más cerca de la API de DOM). Usamos un objeto como el valor del atributo de style . Ese objeto define los estilos como si lo estuviéramos haciendo con JavaScript (porque lo somos).

Incluso puede usar un elemento Reaccionar dentro de JSX, porque eso también es una expresión. Recuerde, un elemento Reaccionar es esencialmente una llamada a función:

 // Ejemplo 7 - Usar un elemento React dentro de {} 
 // https://jscomplete.com/repl?j=SkTLpjYr-
 const MaybeError = ({errorMessage}) => 
 <div> 
 {errorMessage && <ErrorDisplay message = {errorMessage} />} 
 </ div>; 

 // El componente MaybeError usa el componente ErrorDisplay: 
 const ErrorDisplay = ({mensaje}) => 
 <div style = {{color: 'red', backgroundColor: 'yellow'}}> 
 {mensaje} 
 </ div>;
 // Ahora podemos usar el componente MaybeError: 
 ReactDOM.render ( 
 <MaybeError 
 errorMessage = {Math.random ()> 0.5? 'No está bien' : ''} 
 />, 
 mountNode 
 );

El componente MaybeError anterior solo mostraría el componente ErrorDisplay si se le pasa una cadena errorMessage y un div vacío. Reaccionar considera {true} , {false} , {undefined} y {null} para ser elemento válido hijos, que no rinden nada.

También puede usar todos los métodos funcionales de JavaScript en colecciones ( map , reduce , filter , concat , etc.) dentro de JSX. De nuevo, porque devuelven expresiones:

 // Ejemplo 8 - Usar un mapa de matriz dentro {} 
 // https://jscomplete.com/repl?j=SJ29aiYH-
 const Doubler = ({value = [1, 2, 3]}) => 
 <div> 
 {value.map (e => e * 2)} 
 </ div>;
 // Úsalo 
 ReactDOM.render (<Doubler />, mountNode);

Observe cómo le di al value prop un valor por defecto más arriba, porque todo es solo Javascript. Tenga en cuenta también que di salida a una expresión de matriz dentro de div . Reaccionar está bien con eso; Colocará cada valor duplicado en un nodo de texto.

Fundamental # 4: Puede escribir componentes Reaccionar con clases de JavaScript

Los componentes de funciones simples son excelentes para necesidades simples, pero a veces necesitamos más. React también admite la creación de componentes a través de la sintaxis de clases de JavaScript . Aquí está el componente Button (en el Ejemplo 1) escrito con la sintaxis de la clase:

 // Ejemplo 9 - Crear componentes usando clases de JavaScript 
 // https://jscomplete.com/repl?j=ryjk0iKHb
 El botón de clase se extiende React.Component { 
 render () { 
 return <button> {this.props.label} </ button>; 
 } 
 }
 // Úselo (misma sintaxis) 
 ReactDOM.render (<Button label = "Guardar" />, mountNode);

La sintaxis de clase es simple. Defina una clase que amplíe React.Component (otra API de React de nivel superior que necesita aprender). La clase define una función de instancia render() , y esa función de representación devuelve el elemento DOM virtual. Cada vez que utilizamos el componente basado en clases de Button anterior (por ejemplo, al hacer <Button ... /> ), React instanciará un objeto de este componente basado en clase y usará ese objeto para representar un elemento DOM en el árbol DOM .

Esta es la razón por la que usamos this.props.label dentro de JSX en la salida representada arriba. Debido a que cada elemento procesado a través de un componente de clase obtiene una propiedad de instancia especial llamada props que mantiene todos los valores pasados ??a ese elemento cuando se creó.

Como tenemos una instancia asociada con un solo uso del componente, podemos personalizar esa instancia como lo deseamos. Podemos, por ejemplo, personalizarlo después de que se construye utilizando la función de constructor JavaScript normal:

 // Ejemplo 10 - Personalización de una instancia de componente 
 // https://jscomplete.com/repl?j=rko7RsKS-
 El botón de clase se extiende React.Component { 
 constructor (accesorios) { 
 super (apoyos); 
 this.id = Date.now (); 
 } 
 render () { 
 return <button id = {this.id}> {this.props.label} </ button>; 
 } 
 }
 // Úsalo 
 ReactDOM.render (<Button label = "Guardar" />, mountNode);

También podemos definir funciones de clase y usarlas en cualquier lugar que deseemos, incluso dentro de la salida JSX devuelta:

 // Ejemplo 11 - Uso de propiedades de clase 
 // https://jscomplete.com/repl?j=H1YDCoFSb
 El botón de clase se extiende React.Component { 
 clickCounter = 0;
 handleClick = () => { 
 console.log (`Clicked: $ {++ this.clickCounter}`); 
 }; 

 render () { 
 regreso ( 
 <button id = {this.id} onClick = {this.handleClick}> 
 {this.props.label} 
 </ button> 
 ); 
 } 
 }
 // Úsalo 
 ReactDOM.render (<Button label = "Guardar" />, mountNode);

Tenga en cuenta algunas cosas sobre el ejemplo 11 anterior:

  • La función handleClick se escribe usando la nueva sintaxis de campo de clase propuesta en JavaScript. Esto todavía está en la etapa 2, pero por muchas razones es la mejor opción para acceder a la instancia montada en el componente (gracias a las funciones de flecha). Sin embargo, debe usar un compilador como Babel configurado para comprender la etapa 2 (o la sintaxis del campo de clase) para que funcione el código anterior. El jsComplete REPL tiene eso preconfigurado.
  • También hemos definido las variables de instancia de clickCounter usando la misma sintaxis de campo de clase. Esto nos permite omitir el uso de una llamada de constructor de clase por completo.
  • Cuando especificamos la función handleClick como el valor del atributo especial onClick React, no la llamamos. Pasamos en la referencia a la función handleClick . Llamar a la función en ese nivel es uno de los errores más comunes al trabajar con Reaccionar.
 // Incorrecto: 
 onClick = { this.handleClick () }
 // Correcto: 
 onClick = { this.handleClick }

Fundamental # 5: Eventos en Reacción: Dos Diferencias Importantes

Al manejar eventos dentro de los elementos de React, hay dos diferencias muy importantes con respecto a la forma en que lo hacemos con la DOM API:

  • Todos los atributos de elementos React (eventos incluidos) se nombran usando camelCase , en lugar de minúsculas . Es onClick , no onclick .
  • Pasamos una referencia de función de JavaScript real como el controlador de eventos, en lugar de una cadena. Es onClick={ handleClick } , no onClick=" handleClick" .

Reaccionar envuelve el objeto de evento DOM con un objeto propio para optimizar el rendimiento del manejo de eventos. Pero dentro de un manejador de eventos, aún podemos acceder a todos los métodos disponibles en el objeto de evento DOM. React pasa el objeto de evento envuelto a cada llamada de control. Por ejemplo, para evitar un formulario de la acción de presentación predeterminada, puede hacer lo siguiente:

 // Ejemplo 12 - Trabajar con eventos envueltos 
 // https://jscomplete.com/repl?j=HkIhRoKBb
 class Form extends React.Component { 
 handleSubmit = (event) => { 
 event.preventDefault (); 
 console.log ('Formulario enviado'); 
 }; 

 render () { 
 regreso ( 
 <form onSubmit = {this.handleSubmit}> 
 <button type = "submit"> Enviar </ button> 
 </ form> 
 ); 
 } 
 }
 // Úsalo 
 ReactDOM.render (<Form />, mountNode);

Fundamental # 6: cada componente de React tiene una historia

Lo siguiente se aplica solo al componente de clase (los que extienden React.Component ). Los componentes de función tienen una historia ligeramente diferente.

  1. Primero, definimos una plantilla para Reaccionar para crear elementos del componente.
  2. Luego, instruimos a React para que lo use en alguna parte. Por ejemplo, dentro de una llamada de render de otro componente, o con ReactDOM.render .
  3. Luego, React crea una instancia de un elemento y le da un conjunto de accesorios a los que podemos acceder con this.props . Esos accesorios son exactamente lo que aprobamos en el paso 2 anterior.
  4. Como todo es JavaScript, se llamará al método del constructor (si está definido). Este es el primero de lo que llamamos: métodos del ciclo de vida de los componentes .
  5. Reaccionar luego calcula el resultado del método de representación (el nodo DOM virtual).
  6. Como esta es la primera vez que React representa el elemento, React se comunicará con el navegador (en nuestro nombre, utilizando la DOM API) para mostrar el elemento allí. Este proceso se conoce comúnmente como montaje .
  7. Reaccionar luego invoca otro método de ciclo de vida, llamado componentDidMount . Podemos usar este método para, por ejemplo, hacer algo en el DOM que ahora sabemos que existe en el navegador. Antes de este método de ciclo de vida, el DOM con el que trabajamos era todo virtual.
  8. Algunas historias de componentes terminan aquí. Otros componentes se desmontan del navegador DOM por varias razones. Justo antes de que ocurra lo último, React invoca otro método del ciclo de vida, componentWillUnmount .
  9. El estado de cualquier elemento montado podría cambiar. El padre de ese elemento podría volver a representar. En cualquier caso, el elemento montado podría recibir un conjunto diferente de accesorios. ¡La magia de reacción sucede aquí y realmente comenzamos a necesitar Reacción en este punto! Antes de este punto, honestamente, no necesitábamos Reaccionar.

La historia de este componente continúa, pero antes de que lo haga, debemos entender esta cuestión de estado de la que hablo.

Fundamental # 7: los componentes React pueden tener un estado privado

Lo siguiente también es solo aplicable a los componentes de clase. ¿Mencioné que algunas personas llaman tontos a los componentes de presentación solamente?

La propiedad state es especial en cualquier componente de la clase React. React supervisa cada estado de los componentes para ver los cambios. Pero para que React lo haga de manera eficiente, tenemos que cambiar el campo de estado a través de otra cosa de React API que necesitamos aprender, this.setState :

 // Ejemplo 13 - la API setState 
 // https://jscomplete.com/repl?j=H1fek2KH-
 La clase CounterButton extiende React.Component { 
 estado = { 
 clickCounter: 0, 
 currentTimestamp: new Date (), 
 }; 

 handleClick = () => { 
 this.setState ((prevState) => { 
 return {clickCounter: prevState.clickCounter + 1}; 
 }); 
 }; 

 componentDidMount () { 
 setInterval (() => { 
 this.setState ({currentTimestamp: new Date ()}) 
 }, 1000); 
 } 

 render () { 
 regreso ( 
 <div> 
 <button onClick = {this.handleClick}> Haga clic en </ button> 
 <p> Hace clic: {this.state.clickCounter} </ p> 
 <p> Tiempo: {this.state.currentTimestamp.toLocaleString ()} </ p> 
 </ div> 
 ); 
 } 
 }
 // Úsalo 
 ReactDOM.render (<CounterButton />, mountNode);