React es una librería (no un framework) que falicita el desarrollo de intarfaces de usuario. Permite la crear interfaces complejas utilizando piezas chicas de código, reutilizables, que llamaremos componentes
.
Cuando vamos a comenzar un nuevo proyecto con React, generalmente tenemos que lidiar con una gran cantidad de herramientas como gestores de paquetes, transpiladores, etc. Por esto, el equipo de facebook creo , una herramienta que realizará la configuración necesaria y nos permitirá comenzar a desarrollar con React de una forma rápida y sencilla.
-
Lo primero que tenemos que hacer es instalar la herramienta con NPM
npm i -g create-react-app
-
Una vez instalada, dentro de la carpeta donde queremos crear un proyecto nuevo, podemos ejecutar:
create-react-app first-app
-
Este último comando creará una carpeta
first-app
(esta tarea puede demorar unos cuantos minutos), y dentro todo el contenido necesario para ejecutar nuestra primer app con React. -
Una vez que finalizó el proceso anterior, podemos ingresar a la carpeta
cd first-app
-
Dentro de la carpeta, vamos a tener el siguiente contenido (que puede variar un poco dependiendo de la versión de
create-react-app
):first-app/ .gitignore package-lock.json package.json README.md node_modules/ public/ favicon.ico index.html manifest.json src/ App.css App.js App.test.js index.css index.js logo.svg serviceWorker.js
- node_modules: es la carpeta donde se guardan las dependencias del proyecto
- public: es la carpeta raíz del proyecto donde se encuentra el index.html
- src: es el directorio donde vamos a colocar los archivos de nuestros componentes
-
Para ejecutar la app de ejemplo que configura por defecto
create-react-app
, solo tenemos que ejecutarnpm start
. Una vez que termine, vamos a poder acceder desde un navegador a la direcciónlocalhost:3000
y ver algo similar (ya que depende de la versión) a lo siguiente:
-
El punto de entrada a la aplicación es el archivo
scr/index.js
. Acá se inicializa el componente principal App.js, a través del método ReactDOM.Render. Dicho método recibe como primer parámetro el componente a renderizar y como segundo el elemento del DOM donde el componente va ser renderizado:import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; // recibe como primer parametro el componente App, y como segundo en que elemento del DOM quiero que sea vea ReactDOM.render(<App />, document.getElementById('root')); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: http://bit.ly/CRA-PWA serviceWorker.unregister();
-
Y el componente App, nuestro componente principal y donde vamos a comenzar el desarrollo, tiene el siguiente código:
import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; class App extends Component { render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.js</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div> ); } } export default App;
-
Para seguir con la teoría, vamos a limpiar un poco el código, dejándolo lo más limpio posible:
import React, { Component } from 'react'; class App extends Component { render() { return ( <div> <h1>¡Hola Mundo!</h1> </div> ); } } export default App;
-
El código del ejemplo desarrollado hasta ahora se encuentra dentro de la carpeta ejemplos/01-first-app
- La sintáxis de HTML que vemos en el componente App, no es realmente HTML, sino JSX. Es una sintáxis extendida de Javascript que nos permite escribir código JS con etiquetas del estilo HTML.
- Al igual que en HTML, las etiquetas que usamos en JSX pueden tener atributos y elementos hijos. Si un atributo está escrito entre llaves
{}
, entonces el valor es una expresión de Javascript. - Aclaración: JSX no usa comillas para encerrar a las etiquetas de HTML
Ejemplo:
class App extends Component {
render() {
return (
<div>
<h1>¡Hola Mundo!</h1>
</div>
);
}
}
- Como JSX es más cercano a Javascript que HTML, React usa camelCase como convención para las propiedades en lugar de los atributos HTML.
- Por ejemplo, el atributo
class
se convierte esclassName
,tabindex
se convierte entabIndex
yonclick
se convierte enonClick
(entre otros). - Vamos a modificar un poco nuestro ejemplo del
Hola Mundo
Ejemplo:
class App extends Component {
render() {
const elementoH1 = <h1>¡Hola Mundo!</h1>
return (
<div>
{elementoH1}
</div>
);
}
}
- Movimos, a una variable llamada
elementoH1
, una porción del código JSX. - Para que JSX funcione, es necesario que todos los elementos que usemos esten encerrados en una sola etiqueta padre.
- La expresiones dinámicas, o expresiones de Javascript, se engloban dentro de las llaves
{}
Ejemplo 1:
class App extends Component {
render() {
const nombre = 'Ada'
return (
<div>
<h1>¡Hola {nombre}!</h1>
</div>
);
}
}
Ejemplo 2:
class App extends Component {
render() {
const persona = {
nombre: 'Ada',
apellido: 'Lovelace'
}
return (
<div>
<h1>¡Hola {persona.nombre} {persona.apellido}!</h1>
</div>
);
}
}
- Los componentes permiten separar la web en piezas inependientes y reutilizables. Son los ladrillos de cualquier aplicación desarrollada con React, y una aplicación típica puede tener muchos.
- En pocas palabras, un componente es una clase o función de Javascript que puede aceptar algún input (llamados
props
) y retorna elementos de React, describiendo como una sección de la UI debería verse.
- Creá un archivo llamaddo
Hola.js
dentro de la carpeta./src
- Copia el siguiente contenido:
import React, { Component } from 'react';
class Hola extends Component {
render() {
return (
<div>
<h1>¡Hola mundo!</h1>
</div>
);
}
}
export default Hola;
- Para poder ver el componente que creamos, vamos a cambiar el contenido de
App.js
. - En JSX, los elementos HTML (etiquetas) también representan a los componentes definidos por el usuario.
- Debería quedar de la siguiente forma:
import React, { Component } from 'react';
// acá estamos "importando" el componente "Hola"
import Hola from './Hola'
class App extends Component {
render() {
return (
<div>
{/* para usar el componente, podemos usarlo como cualquier otra etiqueta de HTML, recordando que el nombre comienza con mayuscula */}
<Hola />
</div>
);
}
}
export default App;
-
En la segunda línea estamos
importando
nuestro componente. -
En la línea
<Hola />
estamos utilizando nuestra "etiqueta" propia. -
Veamos que pasó en este ejemplo:
- En la función
render()
de App utilizamos la etiqueta<Hola />
. - React llama/ejecuta al componente
Hola
. - Nuestro componente
Hola
retorna un elemento como resultado de la funciónrender()
- En la función
-
IMPORTANTE: Los nombre de los componente siempre tienen que empezar con mayúscula.
-
El código de este ejemplo está en
./ejemplos/02-components
- Hay dos tipos de datos en React:
props
ystate
- La diferencia es complicada de ver al principio, pero con la práctica se va a empezar a notar más la separación entre cada término
- La principal diferencia es que el estado es privado y solo puede ser modificado por el mismo componente.
- Las propiedades son externas, y no pueden ser editadas por el componente que las recibe.
- Las
props
son información pasadas para abajo desde un componente padre a un componente hijo. - Entonces, mientras que las
props
no pueden ser modificadas directamente (si se pueden modificar indirectamente, que lo vamos a ver más adelante), si puede modificar su propio estado.
- Las propiedades de un componente pueden definirse como atributos de configuración de ese componente
- Las propiedades son recibidas desde un nivel superior y son inmutables
- Esto quiere decir que las propiedades se pasan de un componente padre a un componente hijo
- Para acceder a la información que nos llega en una propiedad, vamos a utilizar una variable
props
Ejemplo:
- Si continuamos con el ejemplo anterior
02-components
- Si queremos que el mensaje sea
¡Hola {nombre}!
, y la variable nombre sea configurable, podemos pasarsela mediante una prop - Vamos a modificar un poco el código del componente
Hola
:
import React, { Component } from 'react';
class Hola extends Component {
render() {
return (
<div>
{/* en lugar de poner un nombre fijo, utilizamos un nombre que nos va a llegar a traves de un atributo nombre*/}
<h1>¡Hola {this.props.nombre}!</h1>
</div>
);
}
}
export default Hola;
- Las
props
no son más que los atributos de un elemento, y pueden ser de cualquier tipo de dato - Entonces, en
App.js
, el código quedaría
class App extends Component {
render() {
const elNombreQueQueremosMostrar = 'Ada Lovelace'
return (
<div>
<Hola nombre={elNombreQueQueremosMostrar} />
</div>
)
}
}
- Con las llaves, a nuestro atributo
nombre
le estamos pasando una expresión de JS (en este caso, el valor de una variable) - Si a un atributo le queremos pasar simplemente un texto/string, podemos usar las comillas
class App extends Component {
render() {
return (
<div>
<Hola nombre="Ada Lovelace" />
</div>
)
}
}
- El código de este ejemplo está en
./ejemplos/03-components-props
- Otra forma de guardar información en React, es utilizando el estado del componente.
- A diferencia de las
props
(que son inmutables), el estado es mutable. - Entonces si la información de tu aplicación va a cambiar (por ejemplo, basada en interacciones con el usuario), tiene que ser almacenada en el estado de algún componente.
- Como el estado es privado de solo un componente, no puede compartirse para abajo con componentes hijos.
- Si necesitamos pasar información a comopnentes hijos, tenemos que pasarselas mediante
props
- Para inicializar el estado, vamos a hacerlo en una nueva función
constructor
dentro del componente que queremos que tengastate
Ejemplo:
// el import es una funcion nativa de JS que me permite importa modulos o librerias
import React, { Component } from 'react';
// class y el nombre del componente (en mayúscula)
class App extends Component {
// creamos la funcion constructor con el parametro props
constructor (props) {
// llamamos a la funcion super pasandole las props
super(props)
// creamos el estado del componente
// el estado es simplemente un objeto de JS que va a guardar datos para operar o mostrar en el render
// con el 'this' nos referimos estrictamente al componente que estamos creando
// entonces a nuestro componente App le creamos un estado
// la propiedad SIEMPRE tiene que llamarse state, y el objeto puede tener cualquier cantidad y tipo de propiedades
this.state = {
nombre: 'Ada Lovelace'
}
}
render() {
return (
<div>
{/* para mostrar una propiedad del estado, siempre accedemos con 'this.state' y el nombre de la propiedad que queremos ver */}
<h1>¡Hola, {this.state.nombre}!</h1>
</div>
)
}
}
// export del componente que acabamos de crear
// tiene que ser el mismo nombre que el que definimos despues del 'class'
export default App;
- Para modificar el estado, vamos a utilizar la función
setState
- La función recibe por parámetro un objeto con las propiedades que queremos modificar
Ejemplo:
- Agregamos un setTimeout para ejecutar un cambio de state a los 5 segundos
- Cuando
mutamos
el nombre, se va a modificar automáticamente el DOM, por lo que no tenemos que hacer nada para ver el cambio en la web
// el import es una funcion nativa de JS que me permite importa modulos o librerias
import React, { Component } from 'react';
// class y el nombre del componente (en mayúscula)
class App extends Component {
// creamos la funcion constructor con el parametro props
constructor (props) {
// llamamos a la funcion super pasandole las props
super(props)
// creamos el estado del componente
// el estado es simplemente un objeto de JS que va a guardar datos para operar o mostrar en el render
// con el 'this' nos referimos estrictamente al componente que estamos creando
// entonces a nuestro componente App le creamos un estado
// la propiedad SIEMPRE tiene que llamarse state, y el objeto puede tener cualquier cantidad y tipo de propiedades
this.state = {
nombre: 'Ada Lovelace'
}
// a los 5 segundos ejecutamos una funcion que edita la propiedad `nombre` del state
// pasamos la funcion como una arrow function para que el `this` que utilizamos adentro siga funcionando bien
// despues de ejecutarse la funcion setState, se vuelve a ejecutar la funcion render() que tenemos abajo y modifica lo que vemos en el html
setTimeout(() => {
this.setState({
nombre: 'Grace Hopper'
})
}, 5000)
}
render() {
// bind
return (
<div>
{/* para mostrar una propiedad del estado, siempre accedemos con 'this.state' y el nombre de la propiedad que queremos ver */}
<h1>¡Hola, {this.state.nombre}!</h1>
</div>
)
}
}
// export del componente que acabamos de crear
// tiene que ser el mismo nombre que el que definimos despues del 'class'
export default App;
-
El ejemplo de esta sección está en ./ejemplos/06-components-set-state
-
Los componentes que tienen estado, se denominan stateful components.
-
Tenemos que tratar de tener la menor cantidad de stateful components posible, y tratar de minimizar la información que guardamos en ese estado.
-
Si un componente abajo en la jerarquía (algún componente hijo) necesita acceder a información del estado, podemos pasarla con las
props
-
Para definir en donde vamos a crear un estado, podemos hacernos las siguientes preguntas:
- Identificar cada componente que pinta/renderea/muestra algo basado en el estado
- Encontrar un componente dueño en común que guarda el estado y pase la información a los hijos
- El componente en común o un componente más arriba puede ser el dueño del estado
- Si no encontrás un componente donde poner el estado, podés crear un componente nuevo que guarde el estado y agregarlo en la jerarquía de componentes por arriba de los componentes en común, pasándole la información a los hijos vía
props
.
- Manejar eventos con React es bastante similar a como lo hacíamos con el DOM, pero con algunas diferencias de sintáxis.
- Todos los atributos de cada elemento (eventos incluídos) se nombran usando camelCase, en lugar de lowercase. Entonces, por ejemplo, utilizaríamos
onClick
, en lugar deonlick
. - Vamos a pasar una referencia a una función como handler de un evento, en lugar de un string. Por ejemplo, vamos a tener
onClick={this.handleClick}
en lugar deonClick="handleClick"
.
Ejemplo:
// el import es una funcion nativa de JS que me permite importa modulos o librerias
import React, { Component } from 'react';
// class y el nombre del componente (en mayúscula)
class App extends Component {
// creamos la funcion constructor con el parametro props
constructor (props) {
// llamamos a la funcion super pasandole las props
super(props)
// es importante agregar esta línea cuando pasamos funciones por atributos
// esto es para que si dentro de la funcion utilizamos la palabra reservada `this`, funcione bien y sin problemas
// si nos olvidamos esta linea, `this` dentro de la funcion seria `undefined`
this.handleClick = this.handleClick.bind(this);
}
handleClick () {
alert('¡Hola mundo!')
}
render() {
// bind
return (
<div>
{/* para mostrar una propiedad del estado, siempre accedemos con 'this.state' y el nombre de la propiedad que queremos ver */}
<button onClick={this.handleClick}>¡Saludar!</button>
</div>
)
}
}
// export del componente que acabamos de crear
// tiene que ser el mismo nombre que el que definimos despues del 'class'
export default App;
-
El ejemplo de esta sección está en ./ejemplos/06-events-handler
-
En el constructor() agregamos la linea
this.handleClick = this.handleClick.bind(this)
. -
Esto es importante que este por cada función que definamos y utilicemos en eventos o atributos de nuestros elementos.
-
Si no agregamos esta línea y dentro de la función utilizamos
this
, entonces en ese casothis
va a serundefined
. -
Hay otras 2 formas de solucionar este mismo problema, que veremos más adelante.
-
En las funciones que ejecutamos en eventos, también podemos modificar el estado.
Ejemplo 2
- En este ejemplo vamos a hacer una aplicación donde tengamos un botón con un texto dinámico
- Cada vez que hacemos click en el botón, tenemos que prender o apagar el botón (mostrar ON si está encendido u OFF si está apagado)
- Para esto, podemos utilizar una variable boolean que marquemos como true o false en cada click
class App extends Component {
// creamos la funcion constructor con el parametro props
constructor (props) {
// llamamos a la funcion super pasandole las props
super(props);
// creo el estado con una propiedad isToggleOn para saber si tengo que mostrar on o off en el texto del boton
this.state = {
isToggleOn: true
};
// es importante agregar esta línea cuando pasamos funciones por atributos
// esto es para que si dentro de la funcion utilizamos la palabra reservada `this`, funcione bien y sin problemas
// si nos olvidamos esta linea, `this` dentro de la funcion seria `undefined`
this.handleClick = this.handleClick.bind(this);
}
handleClick () {
// cambio el estado, asignandola el contrario de lo que tenía antes
// si isToggleOn es true, pasa a false
// si isToggleOn es false, pasa a true
this.setState({
isToggleOn: !this.state.isToggleOn
});
}
render() {
return (
<div>
{/* para mostrar una propiedad del estado, siempre accedemos con 'this.state' y el nombre de la propiedad que queremos ver */}
{/* en este caso, depende si la propiedad isToggleOn es true o false, muestro el texto ON u OFF */}
<button onClick={this.handleClick}>{this.state.isToggleOn ? 'ON' : 'OFF'}</button>
</div>
)
}
}
Los ejemplos completos de esta sección están en: ./ejemplos/07-components-set-state-click, ./ejemplos/08-counter, ./ejemplos/09-countdown