React, parte 3: Props

En la publicación pasado vimos creamos nuestro primer componente. En esta ocasión vamos a ver la relación entre dos componentes y cómo estos pueden comunicarse.

Instalar dependencia de bootstrap

En muchos de los casos resolveremos problemas con la ayuda de código externo. En esta ocasión vamos a necesitar de Bootstrap 5 para darle estilos a nuestros componentes.

Opción 1: Instalar bootstrap por CDN

Podemos simplemente abrir el archivo index.html y agregar:

Css:

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">

Js:

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>

Opción 2

Vamos a visitar la siguiente página:

React Bootstrap

Aquí podremos familiarizarnos muy bien con el manejo de Bootstrap en un proyecto React.

Primero vamos a instalar bootstrap mediante npm.

Ingresamos en nuestro proyecto con cd:

cd mi-primer-proyecto-react

(Importante si ya hay una consola abierta con el servidor corriendo, abrir otra consola)

Instalar bootstrap:

npm install react-bootstrap bootstrap

Una vez finalizada la instalación vamos a agregar al archivo main.js:

import 'bootstrap/dist/css/bootstrap.min.css'

El código finalmente quedará:

import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import 'bootstrap/dist/css/bootstrap.min.css'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    {/* Acá vamos a agregar un componente. */}
  </React.StrictMode>,
)

Para que Bootstrap no entre en conflicto con el css que viene por defecto con React, vamos a entrar al archivo: index.css, y a eliminar todo el código. Dejaremos este archivo por si necesitamos reforzar estilos.

Ejemplo

Nuestro ejercicio será simple: una lista de canciones con el nombre, artista que lo interpreta, una foto de este último y un enlace para escuchar la canción en Spotify:

(Imágenes extraídas de https://open.spotify.com/intl-es)

Para ello dentro de la carpeta src/components vamos crear un componente llamado: CancionesList.jsx con el siguiente código:

export default function CancionesList() {

    return (
        <div className="row">
            <div className="col-sm-4">
                <div className="card">
                    <img src="https://i.scdn.co/image/ab67616d00001e0284243a01af3c77b56fe01ab1" className="card-img-top m-3" style={{"maxWidth": "150px"}} alt="The Beatles" />
                    <div className="card-body">
                        <h2 className="card-title"> Let it be </h2>
                        <h3 className="card-subtitle mb-2 text-muted"> The beatles </h3>
                        <a target="_blank" href="https://open.spotify.com/intl-es/track/7iN1s7xHE4ifF5povM6A48?si=4add2e6c3d124fae" className="card-link"> Escuchar </a>
                    </div>
                </div>
            </div>
            <div className="col-sm-4">
                <div className="card">
                    <img src="https://i.scdn.co/image/ab67616d00001e02e8b066f70c206551210d902b" className="card-img-top m-3" style={{"maxWidth": "150px"}} alt="Queen" />
                    <div className="card-body">
                        <h2 className="card-title"> I want to break free </h2>
                        <h3 className="card-subtitle mb-2 text-muted"> Queen </h3>
                        <a target="_blank" href="https://open.spotify.com/intl-es/track/7iAqvWLgZzXvH38lA06QZg?si=173ade5b19384ae4" className="card-link"> Escuchar </a>
                    </div>
                </div>
            </div>
            <div className="col-sm-4">
                <div className="card">
                <img src="https://i.scdn.co/image/ab67616d00001e026f8c26346723dd0531696bed" className="card-img-top m-3" style={{"maxWidth": "150px"}} alt="Janis Japlin" />
                    <div className="card-body">
                        <h2 className="card-title"> Cry Baby </h2>
                        <h3 className="card-subtitle mb-2 text-muted"> Janis Joplin </h3>
                        <a target="_blank" href="https://open.spotify.com/intl-es/track/3L60Vu9qmY6fg2QroRIxgi?si=db9f977d99974e10" className="card-link"> Escuchar </a>
                    </div>
                </div>
            </div>
        </div>
    )

}

Y vamos a agregar el componente a main.jsx:

import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import 'bootstrap/dist/css/bootstrap.min.css'
import CancionesList from './components/CancionesList';

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <div className="container">
      <h1> Lista de canciones </h1>
      <CancionesList />
    </div>
  </React.StrictMode>,
)

Componentes padres / componentes hijos

Hasta acá todo bien, sin embargo, React tiene otra filosofía, la de dividir el código reutilizable en distintos componentes.

Es decir, nosotros tenemos un componente que dentro suyo tiene tres cards, podrían ser menos o más, pero lo lógico sería que que cada una de éstas pueda ser creada como componente, y de esta manera su código podrá ser reutilizable e independiente.

Entonces vamos a crear un subcomponente al que yo voy a llamar: CancionItem.jsx

Vamos a copiar alguno de los elementos card como modelo:

export default function CancionItem() {

    return (
        <div className="card">
            <img src="https://i.scdn.co/image/ab67616d00001e0284243a01af3c77b56fe01ab1" className="card-img-top m-3" style={{ "maxWidth": "150px" }} alt="The Beatles" />
            <div className="card-body">
                <h2 className="card-title"> Let it be </h2>
                <h3 className="card-subtitle mb-2 text-muted"> The beatles </h3>
                <a target="_blank" href="https://open.spotify.com/intl-es/track/7iN1s7xHE4ifF5povM6A48?si=4add2e6c3d124fae" className="card-link"> Escuchar </a>
            </div>
        </div>
    )

}

Props

Las props o properties (propiedades) nos serán de mucha utilidad para que un componente padre pueda pasarle información dinámica a un componente hijo.

Para eso debemos agregar a:

export default function CancionItem()

Las props:

export default function CancionItem(props)

Props será un objeto al cual vamos a pasarle propiedades, podemos reemplazar:

<h3 className="card-subtitle mb-2 text-muted"> The beatles </h3>

Por:

<h3 className="card-subtitle mb-2 text-muted"> {props.artista} </h3>

Y luego al llamar al componente:

<CancionItem artista="The Beatles" />

Desestructuración

Una alternativa a pasar parámetros como JSON es la desestructuración.

Por ejemplo:

const json = {
  cancion: "Let it be",
  artista: "The Beatles"
}
const cancion = json.cancion;
const artista = json.artista;
console.log(cancion);
console.log(artista);

Podríamos reemplazarlo por:

const json = {
  cancion: "Let it be",
  artista: "The Beatles"
}
const {cancion, artista} = json;
console.log(cancion);
console.log(artista);

En la cuarta línea vamos a crear variables para guardar cada una de las propiedades del objetos.

Al pasar props se suele desestructuar:

export default function CancionItem({cancion, artista, img_url, cancion_url})

El código finalmente quedará:

export default function CancionItem({cancion, artista, img_url, cancion_url}) {

    return (
        <div className="card">
            <img src={img_url} className="card-img-top m-3" style={{ "maxWidth": "150px" }} alt={artista} />
            <div className="card-body">
                <h2 className="card-title"> {cancion} </h2>
                <h3 className="card-subtitle mb-2 text-muted"> {artista} </h3>
                <a target="_blank" href={cancion_url} className="card-link"> Escuchar </a>
            </div>
        </div>
    )

}

Entonces podemos llamar al componente pasándole la información de la siguiente manera:

<CancionItem 
cancion="Let it be"
artista="The beatles"
img_url="https://i.scdn.co/image/ab67616d00001e0284243a01af3c77b56fe01ab1"
cancion_url="https://open.spotify.com/intl-es/track/7iN1s7xHE4ifF5povM6A48?si=4add2e6c3d124fae"
/>

El código del componente CancionesList finalmente quedará:

import CancionItem from "./CancionItem"

export default function CancionesList() {

    return (
        <div className="row">
            <div className="col-sm-4">
                <CancionItem 
                    cancion="Let it be"
                    artista="The beatles"
                    img_url="https://i.scdn.co/image/ab67616d00001e0284243a01af3c77b56fe01ab1"
                    cancion_url="https://open.spotify.com/intl-es/track/7iN1s7xHE4ifF5povM6A48?si=4add2e6c3d124fae"
                />
            </div>
            <div className="col-sm-4">
                <CancionItem 
                    cancion="I want to break free"
                    artista="Queen"
                    img_url="https://i.scdn.co/image/ab67616d00001e02e8b066f70c206551210d902b"
                    cancion_url="https://open.spotify.com/intl-es/track/7iAqvWLgZzXvH38lA06QZg?si=173ade5b19384ae4"
                />
            </div>
            <div className="col-sm-4">
                <CancionItem 
                    cancion="Cry Baby"
                    artista="Janis Joplin"
                    img_url="https://i.scdn.co/image/ab67616d00001e026f8c26346723dd0531696bed"
                    cancion_url="https://open.spotify.com/intl-es/track/3L60Vu9qmY6fg2QroRIxgi?si=db9f977d99974e10"
                />
            </div>
        </div>
    )

}

Alternativa componentes de react-bootstrap.

React-bootstrap trae consigo una lista de componentes como alternativa. Por ejemplo podríasmos reemplazar:

<div className="card">
	...
</div>

Por:

import Card from 'react-bootstrap/Card';

<Card>
	...
</Card>

Vamos a rehacer los componentes de la siguiente forma:

main.jsx:

import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import 'bootstrap/dist/css/bootstrap.min.css';
import CancionesList from './components/CancionesList';
import { Container } from 'react-bootstrap';

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <Container>
      <h1> Lista de canciones </h1>
      <CancionesList />
    </Container>
  </React.StrictMode>,
)

CancionesList.jsx:

import CancionItem from "./CancionItem"
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';

export default function CancionesList() {

    return (
        <Row>
            <Col sm={4}>
                <CancionItem
                    cancion="Let it be"
                    artista="The beatles"
                    img_url="https://i.scdn.co/image/ab67616d00001e0284243a01af3c77b56fe01ab1"
                    cancion_url="https://open.spotify.com/intl-es/track/7iN1s7xHE4ifF5povM6A48?si=4add2e6c3d124fae"
                />
            </Col>
            <Col sm={4}>
                <CancionItem
                    cancion="I want to break free"
                    artista="Queen"
                    img_url="https://i.scdn.co/image/ab67616d00001e02e8b066f70c206551210d902b"
                    cancion_url="https://open.spotify.com/intl-es/track/7iAqvWLgZzXvH38lA06QZg?si=173ade5b19384ae4"
                />
            </Col>
            <Col sm={4}>
                <CancionItem
                    cancion="Cry Baby"
                    artista="Janis Joplin"
                    img_url="https://i.scdn.co/image/ab67616d00001e026f8c26346723dd0531696bed"
                    cancion_url="https://open.spotify.com/intl-es/track/3L60Vu9qmY6fg2QroRIxgi?si=db9f977d99974e10"
                />
            </Col>
        </Row>
    )

}

CancionItem.jsx:

import Card from 'react-bootstrap/Card';

export default function CancionItem({cancion, artista, img_url, cancion_url}) {

    return (

        <Card>
            <img src={img_url} className="card-img-top m-3" style={{ "maxWidth": "150px" }} alt={artista} />
            <Card.Body>
                <h2 className="card-title"> {cancion} </h2>
                <h3 className="card-subtitle mb-2 text-muted"> {artista} </h3>
                <a target="_blank" href={cancion_url} className="card-link"> Escuchar </a>
            </Card.Body>
        </Card>
        
    )

}