Nel capitolo precedente, abbiamo visto come utilizzare gli elementi BrowserRouter
, Routes
e Route
forniti dai corrispondenti componenti della libreria React Route.
Abbiamo visto come definire le route che associano i componenti Blog
e Post
ai corrispondenti URL tramite gli attributi path
e element
degli elementi Route
.
Il file Blog.js
La prima route associa la pagina iniziale dell'app (path="/"
) al componente Blog
. Non dovremo modificare nulla nel file Blog.js
e per questo ci limitiamo a riportarne il codice:
import { useEffect, useState } from "react";
import axios from "axios";
import PostItem from "./PostItem";
const url = 'https://developer.wordpress.org/news/wp-json/wp/v2/posts?_fields=id,slug,date,title,excerpt,content,link,_links&_embed=author,wp:featuredmedia';
export default function Blog() {
const [posts, setPosts] = useState([]);
useEffect(() => {
axios
.get(url)
.then(res => {
console.log(res.data);
setPosts(res.data);
})
.catch(error => console.log(error));
}, []);
return (
<div className="blog">
{posts.map(item => (
<PostItem
key={item.id}
post={item}
/>
))}
</div>
);
}
Saranno invece necessarie alcune piccole modifiche al file PostItem.js
. Qui dovremo sostituire l'elemento <a>
che punta alla risorsa originaria con un elemento <Link>
.
I link
La documentazione di React Router definisce un <Link>
come un elemento che permette all'utente di navigare ad un'altra pagina con un clic. In react-router-dom
, un <Link>
rende accessibile un elemento <a>
con un attributo href
che punta ad una data risorsa. Questo significa che operazioni come un clic destro potrebbero non avere l'effetto atteso.
Inoltre,
Un valore
<Link to>
relativo (che non inizia con/
) si risolve rispetto alla route principale, il che significa che si basa sul percorso dell'URL a cui corrispondeva la route che ha reso quel<Link>
. Può contenere..
per collegarsi a percorsi più in alto nella gerarchia. In questi casi,..
funziona esattamente come la funzionecd
della riga di comando; ogni..
rimuove un segmento del percorso principale.
Un <Link>
può avere anche una proprietà state
, che può essere utilizzata per impostare un valore con stato per la nuova posizione che viene archiviato nello stato della cronologia. Sarà quindi possibile accedere a questo valore tramite useLocation()
.
Il file PostItem.js
Chiarito questo, apriamo il file PostItem.js
e importiamo il componente Link
da react-router-dom
:
import { Link } from 'react-router-dom';
Passiamo al listato JSX e sostituiamo l'elemento <a>
con <Link>
:
<h2 className="post-title">
<Link to={`/post/${post.slug}`} state={ { post: { item } } }>
<p dangerouslySetInnerHTML={{ __html: post.title.rendered }} />
</Link>
</h2>
Il valore assegnato all'attributo to
stabilisce la risorsa di destinazione. Il valore di ${post.slug}
viene generato dinamicamente in base al post corrente e permetterà di generare un URL specifico per ogni post restituito dalla Rest API.
Torniamo per un attimo allo script App.js
e analizziamo il codice dell'elemento <Route>
che fa riferimento al componente Post
:
<Route
path='/post/:slug'
element={<Post />}
/>
Ora dovrebbe essere chiaro che il percorso associato al componente Post
si compone di una parte dinamica che genera lo slug del post corrente. Quando l'utente, dalla pagina del blog, farà clic su un link, verrà reso a video il post corrispondente allo slug.
L'immagine che segue mostra l'output di un'anteprima di post generata dal componente PostItem.js
.
L'immagine che segue mostra invece il codice HTML generato dal componente.
Ed ecco il codice completo del file PostItem.js
:
import { useEffect, useState } from "react";
import { Link } from 'react-router-dom';
import axios from "axios";
export default function PostItem( { post } ){
const [featuredImage, setFeaturedImage] = useState('https://images.pexels.com/photos/270404/pexels-photo-270404.jpeg');
const [item, setItem] = useState(post);
const [isLoading, setIsLoading] = useState(true);
const date = new Date(post.date).toLocaleDateString("it-IT", {
year: "numeric",
month: "long",
day: "numeric",
});
useEffect(() => {
axios
.get(post?._links?.["wp:featuredmedia"]?.[0]?.href)
.then(res => {
if(typeof res.data.source_url === "string"){
setFeaturedImage(res.data.source_url);
}
})
.catch(error => console.log(error))
.finally(() => setIsLoading(false))
}, []);
return(
<div className="post-container">
<div className="post-content" id={post.slug}>
<div className="post-featured-image">
{
isLoading ? <p>Loading...</p> : <img src={featuredImage} />
}
</div>
<div className="post-details">
<div className="post-date">
{date}
</div>
<h2 className="post-title">
<Link to={`/post/${post.slug}`} state={ { post: { item } } }>
<p dangerouslySetInnerHTML={{ __html: post.title.rendered }} />
</Link>
</h2>
<div
className="post-excerpt"
dangerouslySetInnerHTML={{ __html: post.excerpt.rendered }}
/>
</div>
</div>
</div>
);
}
Nella prossima lezione, creeremo l'ultimo componente della nostra app: il componente Post
, responsabile del rendering di un oggetto post
.