Para poder realizar este taller ud debe:
Para crear una aplicación nueva en React desde una terminal ejecute el comando npx create-react-app myapp
, donde myapp corresponde al nombre de la aplicación. Esto creará una nueva carpeta denominada myapp.
Para ejecutar la aplicación creada ingrese a la carpeta, abra el proyecto en VSCode o en el editor de su preferencia, y desde una terminal ejecute el comando npm start
.
Esto iniciará un servidor web que escucha peticiones en el puerto 3000 y abrirá una nueva ventana del navegador en la que se desplegará un contenido similar al que aparece en la siguiente imagen:
El archivo principal de la aplicación es src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
En este archivo podemos ver que se importan las librerías React y ReactDOM; el estilo principal de la página (./index.css); el componente principal App, y la librería reportWebVitals que es usada por React para medir el desempeño de la aplicación.
Luego, a la constante root se le asigna el punto inicial donde se renderizará el componente principal de la aplicación. En este caso ese punto inicial es un elemento de la página que tiene como identificador (id) el texto root. Ese id puede ser localizado en el archivo /public/index.html en la parte correspondiente al cuerpo (body) de la página:
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
Ahora vamos a modificar el componente principal usando para esto una constante. Para esto crearemos la constante component
a la que le asignamos el valor
. Luego renderizamos este componente. Hola mundo
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
const root = ReactDOM.createRoot(document.getElementById("root"));
const component = <h1>Hola mundo</h1>;
root.render(component);
Guarde los cambios y vuelva al navegador donde podrá observar que el contenido de la página ha cambiado por un título de primer nivel con el texto Hola mundo.
Note que en este caso el contenido de la constante component
no es un string
pero tampoco es HTML. Esta sintaxis se denomina JSX y es la que usa React para la implementación de los componentes. En este documento puede encontrar información adicional sobre JSX.
La forma más habitual de crear componentes en React es mediante el uso de funciones (lo que también se conoce como componentes funcionales). Para ver un ejemplo revise el contenido del archivo /src/App.js:
Acá podemos ver que tenemos una función denominada App. Note que en este caso la convención de nombramiento para componentes funcionales, inicia con mayúscula (a diferencia de las funciones tradicionales que inician en minúscula).
Esta función retorna una expresión JSX. El componente es entonces un contenedor con la clase App. El contenedor tiene un header, el cual a su vez contiene una imagen, un párrafo y un enlace a la página oficial de React.
Note varias particularidades. A diferencia de HTML, en JSX el atributo de una etiqueta para hacer referencia a una clase no es class sino className dado que class es una palabra reservada de JSX.
La imagen tiene un atributo src que corresponde a la ruta donde está almacenada la imagen. En este caso el valor se obtiene usando una expresión en JSX. Una expresión en JSX corresponde a una expresión válida en JavaScript a la que se envuelve dentro de llaves. Por tanto {logo} hace referencia a la variable logo definida en la primera importación del archivo.
import logo from './logo.svg';
import './App.css';
function App() {
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>
);
}
Para renderizar este componente vamos a modificar el archivo src/index.js así:
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
Lo que hemos hecho es importar el componente App
desde el archivo ./App
(no se requiere ingresar la extensión .js). Luego este componente se renderiza envolviéndolo dentro de una nueva etiqueta HTML
.
Los componentes pueden recibir información para usarla a conveniencia. Esto se hace mediante el uso del objeto props
. Para ejemplificar esto creemos un nuevo componente denominado Post. Para esto cree un nuevo archivo src/Post.js con el siguiente contenido:
function Post(props) {
return (
<div>
<h1>Post</h1>
<h2>Author: {props.author}</h2>
<h2>Content: {props.content}</h2>
<h2>Likes: {props.likes}</h2>
</div>
);
}
export default Post;
Este componente recibe como parámetro el objeto props; luego se usan los elementos de ese objeto para mostrar información acerca del autor, contenido y número de likes de un post.
Para renderizar este componente modificamos el archivo src/index.js así:
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import Post from "./Post";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Post author="John Doe" content="This is the post content" likes={20} />
);
En este caso, en el componente Post se están definiendo tres atributos denominados author, content y likes. A cada atributo se le asigna un valor. Estos valores son capturados por el componente en el objeto props
.
La vista del componente en el navegador quedará así:
Una aplicación web puede crearse mediante el uso y reuso de varios componentes. Reutilicemos el componente anterior así:
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import Post from "./Post";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<div>
<Post author="John Doe" content="This is the post content" likes={20} />
<Post author="Anne Hill" content="I like React" likes={3} />
<Post author="Laia Martins" content="I love JSX" likes={0} />
</div>
);
Acá podemos ver que estamos usando el componente Post tres veces. Cada componente tiene atributos diferentes. Estos tres componentes serán renderizados individualmente en la página.
En el ejemplo anterior cuando un post no tiene likes aparece el texto "Likes: 0". Un diseñador podría tomar la decisión de mostrar un "llamado a la acción" para que el usuario de un like si la publicación aún no tiene. Para esto se podría usar un renderizado condicional.
En este código se crea la constante renderLikes a la cual se asigna una función. Esta función verifica la cantidad de likes que hay en la publicación. Si el número es cero se retorna un mensaje que invita a al usuario dar un like; en caso contrario se retorna el número de likes de la publicación. En el componente se hace ahora el llamado a esa función que hará un renderizado condicional.
function Post(props) {
const renderLikes = () => {
if (props.likes === 0) return "Give us a like";
else return "Likes: " + props.likes;
};
return (
<div>
<h1>Post</h1>
<h2>Author: {props.author}</h2>
<h2>Content: {props.content}</h2>
<h2>{renderLikes()}</h2>
</div>
);
}
export default Post;
Para incluir Bootstrap en React existen varias alternativas. Para este tutorial usaremos la librería react-bootstrap la cual se instala con el comando npm install react-bootstrap bootstrap
.
Luego, en el archivo src/index.js incluimos la siguiente línea:
import 'bootstrap/dist/css/bootstrap.min.css';
Ahora vamos a ajustar el contenido de los posts usando tarjetas (cards) y el layout usando filas (row) y columnas (col).
Modificamos el archivo src/index.js así:
import Container from "react-bootstrap/Container";
import Row from "react-bootstrap/Row";
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import "bootstrap/dist/css/bootstrap.min.css";
import Post from "./Post";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Container className="mt-3">
<Row>
<Post author="John Doe" content="This is the post content" likes={20} />
<Post author="Anne Hill" content="I like React" likes={3} />
<Post author="Laia Martins" content="I love JSX" likes={0} />
</Row>
</Container>
);
Note que estamos incluyendo un contenedor y una fila para desplegar los elementos.
Ahora modificamos el archivo src/Post.js así:
import Button from "react-bootstrap/Button";
import Card from "react-bootstrap/Card";
import Col from "react-bootstrap/Col";
function Post(props) {
const renderLikes = () => {
if (props.likes === 0) return "Give us a like";
else return "Likes" + props.likes;
};
return (
<Col>
<Card style={{ width: "18rem" }}>
<Card.Body className="mb-3">
<Card.Title>{props.author}</Card.Title>
<Card.Text>{props.content}</Card.Text>
<Card.Text>{renderLikes()}</Card.Text>
<Button variant="primary">Like</Button>
</Card.Body>
</Card>
</Col>
);
}
export default Post;
En este archivo cada post es una tarjeta que se ubica en una columna.
Ahora queremos que cuando el usuario haga clic en el botón "Like" el número de likes de cada post se incremente en uno. Para esto necesitamos que el componente reaccione al evento clic del botón.
Iniciaremos creando la constante handleLikes
a la que le asignamos una función. Esta función inicialmente mostrará un mensaje por consola indicando que el botón ha sido presionado.
En el evento onClick
del botón se le asigna la constante handleLikes
.
const handleLikes = () => {
console.log("Button clicked...");
};
return (
<Col>
<Card style={{ width: "18rem" }}>
<Card.Body className="mb-3">
<Card.Title>{props.author}</Card.Title>
<Card.Text>{props.content}</Card.Text>
<Card.Text>{renderLikes()}</Card.Text>
<Button variant="primary" onClick={handleLikes}>
Like
</Button>
</Card.Body>
</Card>
</Col>
);
Ahora cuando se hace clic en cada botón se podrá ver el mensaje en la consola del navegador:
Ahora el objetivo es cambiar el valor de la propiedad likes
. Para hacer esto necesitamos introducir el concepto de estado. Un estado en React es un almacén de datos mutable. De este modo las propiedades (props) se usan para crear una instancia del componente y son inmutables, mientras que los estados actúan en el contexto del componente y son mutables.
La característica más interesante de los estados es que cuando estos cambian, la vista del componente también cambia.
Para el caso de nuestro ejemplo vamos a definir un estado para almacenar el valor de los likes de cada post. En este caso usaremos lo que se conoce como un hook de estado. Un hook no es más que una función que se conecta a características especiales de React.
Iniciamos importando el hook de estado (también conocido como useState
) desde la librería de React.
import { useState } from "react";
Ahora declaramos una variable de estado:
function Post(props) {
const [likes, setLikes] = useState(props.likes);
...
Para declarar a la variable de estado se invoca al hook useState
. Este recibe como parámetro el valor inicial de la variable. Para nuestro caso el valor inicial será lo que está en el atributo likes de props.
useState
retorna una pareja de valores. Por una parte el estado actual y por otra parte una función que se encarga de actualizar el estado. La variable de estado la denominaremos likes
mientras que la función la denominaremos setLikes
. Estos nombres son arbitrarios, sin embargo se recomienda mantener esa convención de nombramiento: variable
, setVariable
.
Ahora vamos a modificar la función handleLikes así:
const handleLikes = () => {
console.log("Button clicked...");
setLikes(likes + 1);
};
Como podemos observar, se invoca a la función setLikes y se pasa como parámetro el nuevo valor para likes. En este caso será el valor anterior incrementado en 1
Finalmente actualizamos la función renderLikes
para que tome como referencia la variable de estado:
const renderLikes = () => {
if (likes === 0) return "Give us a like";
else return "Likes: " + likes;
};
Este será entonces el código completo del componente:
import { useState } from "react";
import Button from "react-bootstrap/Button";
import Card from "react-bootstrap/Card";
import Col from "react-bootstrap/Col";
function Post(props) {
const [likes, setLikes] = useState(props.likes);
const renderLikes = () => {
if (likes === 0) return "Give us a like";
else return "Likes: " + likes;
};
const handleLikes = () => {
console.log("Button clicked...");
setLikes(likes + 1);
};
return (
<Col>
<Card style={{ width: "18rem" }}>
<Card.Body className="mb-3">
<Card.Title>{props.author}</Card.Title>
<Card.Text>{props.content}</Card.Text>
<Card.Text>{renderLikes()}</Card.Text>
<Button variant="primary" onClick={handleLikes}>
Like
</Button>
</Card.Body>
</Card>
</Col>
);
}
export default Post;
Ahora si va al navegador podrá observar que cuando se hace clic en el botón Like de cada post el valor de los likes se incrementa.