Nella lezione precedente abbiamo creato il componente Blog
, che si occupa della resa a video della struttura della pagina che raccoglie le anteprime degli ultimi articoli pubblicati in un sito WordPress.
Il file PostItem.js
In questa lezione creeremo un componente PostItem
che genera le anteprime dei post che vengono visualizzate nella pagina del blog. Nella lezione precedente abbiamo utilizzato PostItem
nel componente Blog
in questo modo:
return (
<div className="blog">
{posts.map(item => (
<PostItem
key={item.id}
post={item}
/>
))}
</div>
}
In pratica, abbiamo utilizzato il metodo map
sull'oggetto posts
invocando PostItem
ad ogni iterazione. Ad ogni elemento PostItem
abbiamo assegnato un attributo key
, necessario all'identificazione dei singoli elementi, e un attributo post
, cui viene assegnato un oggetto item
che rappresenta il post corrente.
Se non l'abbiamo già fatto, all'interno della cartella src/components
del progetto creiamo un file PostItem.js
. Apriamo il file e aggiungiamo il seguente codice:
import { useEffect, useState } from "react";
import axios from "axios";
Con queste due dichiarazioni import
rendiamo disponibili useEffect
, useState
e axios
nel componente corrente (si veda la lezione precedente per una descrizione dettagliata).
La funzione PostItem
Passiamo poi alla funzione PostItem
:
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",
});
...
}
Abbiamo passato alla funzione PostItem
un'istanza dell'oggetto post
e dichiarato tre variabili di stato:
featuredImage
fornirà l'URL dell'immagine di anteprima. Abbiamo impostato il valore predefinito all'URL di un'immagine con licenza d'uso gratuita.item
fornirà l'istanza corrente dell'oggettopost
.isLoading
è un booleano che utilizzeremo per verificare se il caricamento dell'immagine è completo.
La costante date
, infine, fornisce la data di pubblicazione del post in formato locale.
Recupero dei dati
Lo step successivo è il recupero dei dati. Sempre all'interno della funzione PostItem
scriviamo:
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))
}, []);
useEffect
viene eseguito dopo ogni rendering e lo utilizziamo qui insieme ad axios
per il recupero dei dati dell'API.
L'URL dell'immagine viene fornita dalla risorsa post._links.["wp:featuredmedia"].[0].href
. Per evitare errori che interrompano l'esecuzione del codice nel caso in cui manchi l'immagine di anteprima del post, abbiamo utilizzato l'operatore di concatenazione opzionale (?.
). Secondo la definizione del Mozilla Developer Network,
Se l'oggetto a cui si accede o la funzione invocata utilizzando questo operatore è
undefined
onull
, l'espressione va in cortocircuito e restituisceundefined
invece di generare un errore.
Ciò ci consente di testare il tipo della risorsa immagine e verificare che sia una stringa. Ciò significa che l'URL è definita e possiamo assegnarla come valore alla variabile featuredImage
.
Il metodo finally()
di axios
viene sempre eseguito e qui lo utilizziamo per impostare su false
lo stato isLoading
. Questo ci sarà utile per verificare lo stato di caricamento dell'immagine.
Il codice JSX
Infine, il codice JSX che genera le anteprime dei post:
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">
<a href={post.link}>
<p dangerouslySetInnerHTML={{ __html: post.title.rendered }} />
</a>
</h2>
<div
className="post-excerpt"
dangerouslySetInnerHTML={{ __html: post.excerpt.rendered }}
/>
</div>
</div>
</div>
);
In questo codice abbiamo utilizzato l'operatore ternario di JavaScript per testare il valore della variabile isLoading
. Nel caso in cui questa assume valore true
, che vuol dire che il caricamento dell'immagine non è completo, viene reso a video un elemento <p>
con il testo Loading...
. In caso contrario, viene resa l'immagine di anteprima recuperata dall'API.
dangerouslySetInnerHTML
è il sostituto di React di InnerHTML
. In quanto tale, viene utilizzato per rendere a video codice HTML (in questo caso il riassunto del post) senza alterarne la struttura. Ciò vuol dire che il codice HTML viene inserito nella div
così com'è.
Si noti che l'elemento JSX con dangerouslySetInnerHTML
non può avere elementi child, per questo l'elemento a cui lo abbiamo assegnato non ha un tag di chiusura.
È bene tenere conto del fatto che dangerouslySetInnerHTML
può esporre a vulnerabilità cross-site scripting (XSS) e per questo motivo va utilizzato solo quando l'origine dei dati è sicura. Se l'origine non è sicura, è opportuno sanitizzare il codice HTML prima di passarlo a dangerouslySetInnerHTML
(ad esempio utilizzando un sanitizer come DOMPurify).
Nel codice qui sopra, abbiamo utilizzato dangerouslySetInnerHTML
per rendere a video il titolo (dangerouslySetInnerHTML={{ __html: post.title.rendered }}
) e il riassunto (dangerouslySetInnerHTML={{ __html: post.excerpt.rendered }}
) del post corrente.
Salvataggio e verifica
Ora salviamo il file, verifichiamo che il processo sia in esecuzione e torniamo alla pagina Web del progetto. Dovremmo aspettarci un codice HTML simile a quello riportato nell'immagine qui sotto:
La nostra App non è ancora pronta per la visualizzazione, ma possiamo già apprezzarne la struttura HTML. Nella prossima lezione aggiungeremo gli stili che ci permetteranno di creare delle anteprime visivamente curate e un layout a scorrimento orizzontale.