Application Next.js de Recherche de Films

L'Équipe Alphorm
L'Équipe Alphorm 45e lecture en min

Créer une application Next.js universelle pour la recherche de films est une tâche passionnante et enrichissante pour les développeurs web. Dans notre précédent article, nous avons exploré comment utiliser les fonctionnalités de gestion des données de Next.js pour optimiser la performance de vos applications. Aujourd’hui, nous allons vous guider pas à pas à travers le processus de développement d’une application Next.js dédiée à la recherche de films, en utilisant l’API OMDB pour récupérer les données de films. Vous découvrirez comment initialiser votre projet, configurer les dépendances essentielles, et intégrer des fonctionnalités avancées telles que la récupération de données statiques, le routage dynamique et la mise en page réactive. En suivant ce guide, vous serez capable de créer une application Next.js performante et optimisée pour la recherche de films, tout en améliorant vos compétences en développement web.

Présentation du projet de recherche de films Next.js

Dans cette section, nous allons développer une application universelle permettant de rechercher des films dans une base de données en interrogeant le service web de l’API OMDB. Avec cette application, nous pouvons taper le nom d’un film à rechercher, et l’application nous retourne la liste des films disponibles correspondant à notre recherche. Pour cela, nous devons avoir une clé d’API, comme nous l’avons vu dans la section précédente, dans l’onglet « API Key » du site OMDB API. Nous pouvons également en récupérer une nouvelle en remplissant à nouveau le formulaire sur le site.

Clé d'API pour l'application Next.js

Pour créer cette application, nous utiliserons Bootstrap et Font Awesome pour les interfaces utilisateur, et Next.js pour les requêtes et le backend. Étant une application monopage, elle partagera un en-tête et une mise en page communs pour toutes les pages. Pour démarrer, nous allons créer le projet manuellement en suivant les étapes que nous avons déjà explorées dans la section précédente.

Création d'une nouvelle application Next.js

Pour la création de notre application, commençons par nommer notre application. Ensuite, nous devons initialiser le projet avec le fichier package.json en utilisant la commande npm init -y dans le terminal pour générer ce fichier .

Initialisation du package.json pour l'application Next.js

Ensuite, installons les dépendances nextJS, reactJS et react-dom avec la commande : npm install next react react-dom dans le terminal.

Installation de next react react-dom pour l'application Next.js

Voilà pour,les dépendances sont bien installé dans notre application. Ajoutons maintenant les scripts suivant dans le fichier package.json  que nous pouvons acceder dans le site officiel de nextJS via le lien : https://nextjs.org/docs/getting-started/installation

				
					"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
}
				
			

Donc, notre fichier package.json est devenu comme ceci :

				
					{
"name": "next_movies_omdbapi",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"next": "^14.2.4",
"react": "^18.3.1",
"react-dom": "^18.3.1"
}
}
				
			

En lançant notre application Next.js avec la commande npm run dev, le terminal affiche des erreurs car la page de démarrage n’existe pas encore. C’est ce que nous allons créer maintenant. Créons le dossier pages et ajoutons-y le fichier index.js avec le contenu simple comme ceci : pages/index.js

				
					export default function Index(){
return(< h2> Mes films disponible< /h2>)
}
				
			

Démarrons de nouveau notre application Next.js et cela affiche bien le texte dans le navigateur comme ceci :

Premier contenu index.js

Voilà, notre application affiche bien la page. Dans la prochaine section, nous allons interroger l’API pour afficher les données disponibles.

Utilisation de getStaticProps dans une application Next.js

Pour cette partie, il faut d’abord récupérer d’abord la clé d’API dans cet onglet comme nous avons vu dans section précédente et puis copier l’url la requête dans cet onglet :

Page pour récupérer les requêtes de données

Avec la clé d’API, nous pouvons effectuer une requête comme celle-ci : https://www.omdbapi.com/?s=avengers&page=2&apikey=b92a0ec9 . En utilisant le paramètre s pour « search » avec la valeur « avengers » comme titre du film, et en spécifiant la page de recherche avec page=2. J’ai utilisé ma clé d’API, mais vous pouvez utiliser la vôtre pour effectuer cette requête. Pour vérifier son bon fonctionnement, nous pouvons la tester dans notre navigateur.

Résultat de la requête film Avengers

On remarque que l’affichage des résultats n’est pas bien représenté. Nous pouvons le corriger en installant l’extension JSON Viewer Pro dans notre navigateur en accédant à ce lien : https://chromewebstore.google.com/category/extensions?hl=fr&utm_source=ext_sidebar .

Illustration de l'installation de JSON Viewer Pro pour l'application Next.js

Par conséquent, nous obtenons des résultats comme ceci :

Résultats mis en forme grâce à JSON Viewer Pro

Parfait, notre requête affiche bien les résultats correspondants. Maintenant, essayons d’afficher ces résultats dans la console. Voyons comment implémenter le code dans le fichier pages/index.js.

				
					import fetch from "node-fetch";
export default function Index({resulats}){
console.log(resulats)
return(< h2> Mes films disponible< /h2>)
}
export async function getStaticProps() {
const response = await fetch('https://www.omdbapi.com/?s=avengers&page=2&apikey=b92a0ec9');
const data = await response.json();
const resulats = data.Search;
return {
props: {
resulats
}
}
}
				
			

Le composant Index est une fonction React qui reçoit resulats en tant que prop. Il utilise console.log(resulats) pour afficher les résultats dans la console du navigateur.

getStaticProps est une fonction asynchrone spéciale pour l’application NextJS qui est exportée et appelée à la construction de la page. Elle permet de pré-rendre la page en récupérant des données à l’avance.

À l’intérieur de getStaticProps, une requête est faite à l’API OMDB pour rechercher des films avec le titre « avengers » et pour la page 2, en utilisant une clé d’API b92a0ec9.

response.json() transforme la réponse en JSON, puis data.Search extrait les résultats de recherche de films de l’objet JSON retourné par l’API.

Les résultats sont assignés à resulats qui est ensuite retourné comme propriété (props) de l’objet à partir de getStaticProps. Ces props peuvent être utilisés dans le composant Index.

Cela nous donne un résultat come ceci dans le terminal :

Illustration de l'affichage des résultats dans la console pour l'application Next.js

Les résultats s’affichent correctement dans la console. Affichons-les dans le navigateur dans la prochaine section.

Réponse réseau et résultats de l'API dans une application Next.js

Nous allons cibler les films avec leurs identifiants uniques dans le lien https://www.imdb.com. Cet identifiant sera utilisé dans notre code pour accéder à leur profil lorsque nous cliquons sur leur titre affiché dans le navigateur sur le port 3000. Voyons comment modifier le code dans le fichier pages/index.js pour atteindre cet objectif

				
					import Link from "next/link";
import fetch from "node-fetch";
export default function Index({ resultats }) {
console.log(resultats);
return (
<>
< h2>Mes films disponibles< /h2>
<ul>
{resultats.map((resultat) => (
<li key={resultat.imdbID}>
<Link href={`https://www.imdb.com/title/${resultat.imdbID}`} target="_blank">
{resultat.Title}
</Link>
</li>
))}
</ul>
</>
);
}
export async function getStaticProps() {
const response = await fetch('https://www.omdbapi.com/?s=avengers&page=2&apikey=b92a0ec9');
const data = await response.json();
const resultats = data.Search;
return {
props: {
resultats
}
};
}
				
			

Index est une fonction React qui reçoit resultats en tant que prop. Cette prop contient un tableau d’objets représentant les résultats de recherche de films.

Le composant commence par afficher « Mes films disponibles » en tant que titre principal (<h2>).

Ensuite, il crée une liste <ul> où chaque élément <li> représente un film. Le key={resultat.imdbID} aide React à identifier de manière unique chaque élément lors du rendu de la liste.

Chaque titre de film est encapsulé dans un composant Link de NextJS. L’attribut href de Link pointe vers l’URL IMDb spécifique au film, utilisant resultat.imdbID pour construire l’URL dynamiquement. L’attribut target= »_blank » ouvre le lien dans un nouvel onglet

Cela nous donne un résultat :

Liste des films disponibles générés par la requête

Et après avoir cliqué le film Next Avangers : Heroes o Tomorrow, nous pouvons acceder à son profil dans un onglet comme nous pouvons voir avec le visuel suivant :

Next Avengers film accessible par son identifiant dans l'application Next.js

Mise en page et CSS dans une application Next.js

Dans cette section, nous plongerons dans les principes de base du Layout, discutant de la création de structures de page cohérentes et réutilisables qui garantissent une expérience utilisateur optimale. De plus, nous explorerons les techniques avancées pour intégrer CSS, et comment NextJS facilite l’organisation et l’optimisation des styles pour différents composants et pages.

Nous allons maintenant mettre en place les différents composant de notre page pour la mise en page de notre application.

Créons le dossier components et y ajouter les composants nécessaires pour notre application tel que : components/Layout.js. Ce composant a un role de garder nootre application dans une seul page : monopage.

				
					import 'bootstrap/dist/css/bootstrap.min.css';
export default function Layout({ children }) {
return (
<div className="container">
<head>
<title>Recherche de films</title>
<meta name="description" content="Une application pour rechercher des films" />
<link rel="icon" href="/favicon.ico" />
<style id="wpr-lazyload-bg-container"></style><style id="wpr-lazyload-bg-exclusion"></style>
<noscript>
<style id="wpr-lazyload-bg-nostyle">.jet-image-accordion__item-loader span{--wpr-bg-d2b70954-e5ec-45e4-9e03-c598e198773e: url('https://blog.alphorm.com/wp-content/plugins/jet-tabs/assets/images/spinner-32.svg');}.rll-youtube-player .play{--wpr-bg-639d27af-6b11-4141-aa4f-f7f2e523e8da: url('https://blog.alphorm.com/wp-content/plugins/wp-rocket/assets/img/youtube.png');}</style>
</noscript>
<script type="application/javascript">const rocket_pairs = [{"selector":".jet-image-accordion__item-loader span","style":".jet-image-accordion__item-loader span{--wpr-bg-d2b70954-e5ec-45e4-9e03-c598e198773e: url('https:\/\/blog.alphorm.com\/wp-content\/plugins\/jet-tabs\/assets\/images\/spinner-32.svg');}","hash":"d2b70954-e5ec-45e4-9e03-c598e198773e","url":"https:\/\/blog.alphorm.com\/wp-content\/plugins\/jet-tabs\/assets\/images\/spinner-32.svg"},{"selector":".rll-youtube-player .play","style":".rll-youtube-player .play{--wpr-bg-639d27af-6b11-4141-aa4f-f7f2e523e8da: url('https:\/\/blog.alphorm.com\/wp-content\/plugins\/wp-rocket\/assets\/img\/youtube.png');}","hash":"639d27af-6b11-4141-aa4f-f7f2e523e8da","url":"https:\/\/blog.alphorm.com\/wp-content\/plugins\/wp-rocket\/assets\/img\/youtube.png"}]; const rocket_excluded_pairs = [];</script></head>
<header className="bg-dark text-white text-center py-3">
<h1>Application de recherche de films</h1>
</header>
<main className="my-4">
{children}
</main>
<footer className="bg-dark text-white text-center py-3">
<p>© 2024 Recherche de films. Tous droits réservés.</p>
</footer>
</div>
);
}
				
			

Nous importons la feuille de style CSS de Bootstrap pour appliquer les styles de Bootstrap à notre application.

Le composant Layout reçoit children en tant que propriété, ce qui lui permet d’encapsuler le contenu des autres composants ou pages qu’il englobe.

La classe container de Bootstrap est utilisé pour envelopper le contenu et appliquer la mise en page par défaut de Bootstrap.

Ensuite, nous avons une page pour afficher les propriétés de chaque film dans le fichier components/Card .js

				
					import React from "react";
const Card = ({ Title, Year, imdbID, Type, Poster }) => {
return (
<div className="card mb-3">
<div className="row g-0">
<div className="col-md-4">
<img src={Poster} className="img-fluid rounded-start" alt={Title} />
</div>
<div className="col-md-8">
<div className="card-body">
<h5 className="card-title">{Title}</h5>
<p className="card-text">Année : {Year}</p>
<p className="card-text">Type : {Type}</p>
<a href="{`https://www.imdb.com/title/${imdbID}`}" target="_self" className="btn btn-primary" data-wpel-link="internal" rel="follow noopener noreferrer">
Voir sur IMDb
</a>
</div>
</div>
</div>
</div>
);
};
				
			

export default Card;

  • <div className= »card mb-3″> : C’est la classe Bootstrap pour une carte avec une marge inférieure.
  • <div className= »row g-0″> : Une rangée Bootstrap sans espacement entre les colonnes.
  • <div className= »col-md-4″> : Une colonne Bootstrap de taille moyenne (4 colonnes sur 12) pour l’image du film.
  • <img src={Poster} className= »img-fluid rounded-start » alt={Title} /> : L’image du film avec une classe Bootstrap pour la rendre fluide et arrondie.
  • <div className= »col-md-8″> : Une colonne Bootstrap pour le contenu textuel du film.
  • <div className= »card-body »> : Corps de la carte Bootstrap qui contient le titre, l’année, le type et le lien IMDb du film.
  • <h5 className= »card-title »>{Title}</h5> : Titre du film.
  • <p className= »card-text »>Année : {Year}</p> : Année du film.
  • <p className= »card-text »>Type : {Type}</p> : Type du film (par exemple, « movie », « series »).
  • <a href={https://www.imdb.com/title/${imdbID}`} target= »_blank » className= »btn btn-primary »>` : Lien pour voir le film sur IMDb, avec une classe Bootstrap pour un bouton primaire.

Puis, nous avons également le composant List pour afficher les listes de films disponibles après l’envoi de la requête. Component/List.js

				
					import Card from "../components/Card";
const List = (props) => {
if (!props.resultats.length) return <p>Aucun résultat</p>;
return (
<div style={{ height: "calc(100vh - 180px)", overflow: "scroll" }}>
{props.resultats && props.resultats.map((result) => (
<Card key={result.id} {...result} />
))}
</div>
);
};
export default List;
				
			

Nous importons le composant Card depuis le fichier Card.js situé dans le répertoire components pour l’appeler dans le composant List.

Composant List : C’est un composant fonctionnel qui prend props comme argument. Il est utilisé pour afficher une liste de films.

Condition if : Vérifie si props.resultats existe et a une longueur non nulle (props.resultats.length). Si ce n’est pas le cas, il affiche un paragraphe indiquant « Aucun résultat ».

Si des résultats existent (props.resultats est défini et non vide), il mappe chaque result dans props.resultats.

Pour chaque result, il rend un composant Card avec key={result.id} et passe toutes les propriétés de result comme des props ({…result}).

Dans le dossier pages, on a aussi le fichier index.js qui est le fichier de démarrage de notre application. Voyons la modification dans ce ficher pour notre application.

				
					import 'bootstrap/dist/css/bootstrap.min.css';
import fetch from "node-fetch";
import Layout from "../components/Layout";
import List from "../components/List";
export default function Index({ resultats }) {
console.log(resultats);
return (
<Layout>
<main className="my-4">
<h2 id="mes-films-disponibles" class="rb-heading-index-7">Mes films disponibles</h2>
<List resultats={resultats} />
</main>
</Layout>
);
}
export async function getServerSideProps() {
const response = await fetch('https://www.omdbapi.com/?s=avengers&page=2&apikey=b92a0ec9');
const data = await response.json();
const resultats = data.Search;
return {
props: {
resultats
}
};
}
				
			

Layout et List : Importe les composants Layout et List depuis leurs fichiers respectifs dans le répertoire components. Layout est utilisé pour définir la mise en page générale de la page d’index, tandis que List est utilisé pour afficher une liste de films.

Index est un composant reactJS qui reçoit resultats comme propriété déstructurée. resultats est passé à ce composant via getServerSideProps.

<Layout> : Englobe tout le contenu de la page avec le composant Layout, définissant ainsi la structure de base de la page avec un en-tête, un pied de page et un conteneur principal.

<main className= »my-4″> : Utilise la classe Bootstrap my-4 pour ajouter une marge sur le dessus et le bas du contenu principal.

<h2 className= »text-center »>Mes films disponibles</h2> : Un titre centré indiquant « Mes films disponibles ».

<List resultats={resultats} /> : Utilise le composant List pour afficher les résultats (resultats) récupérés de l’API OMDB. Les résultats sont passés comme propriété resultats à List.

getServerSideProps est une fonction asynchrone utilisée par NextJS pour pré-rendre la page sur le serveur à chaque demande. Elle est utilisée pour récupérer des données (dans ce cas, une liste de films Avengers depuis l’API OMDB).

Elle effectue une requête GET à l’API OMDB pour récupérer les résultats de recherche pour « avengers« , page 2, avec une clé API spécifiée.

Les résultats de la recherche (data.Search) sont extraits de la réponse JSON de l’API et sont renvoyés sous forme de props (resultats) à la fonction Index.

Enfin, nous avons le fichier _app.js pour les styles globaux de notre application.

Pages/_app.js

				
					import 'bootstrap/dist/css/bootstrap.min.css';
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />;
}
export default MyApp;
				
			

MyApp est un composant fonctionnel React qui reçoit deux props : Component et pageProps.

Component : C’est le composant de la page actuelle que Next.js doit rendre (par exemple, Index, About, Contact, etc.).

pageProps : Ce sont les propriétés de la page (props) passées à Component par Next.js, généralement utilisées pour passer des données initiales nécessaires au rendu de la page.

Lançons notre application Next.js pour tester si tout fonctionne bien avec la commande habituelle : npm run dev .

Liste des films dans une application Next.js avec layout

Nous arrivons à mettre en forme notre application. Mais, actuellement nous ne pouvons pas encore faire de rechercher de films, c’est ce que nous allons faire dans la section suivante.

Plongez dans l’univers fascinant de Next.js et devenez un expert en 2024 grâce à notre formation vidéo exclusive sur Alphorm : Formation Next.js

Fonctionnalité de recherche dans Next.js

Notre application ne permet pas actuellement de rechercher un film à partir de son titre. Cette section a pour objectif de corriger cela. Nous allons ajouter un champ de saisie (input) pour permettre à l’utilisateur de taper le titre du film recherché. Ensuite, le film sera affiché s’il est trouvé, ou un message indiquera qu’il n’existe pas.

Pour ce faire, créons un fichier Searchbar.js dans le dossier components avec le contenu suivant : components/Searchbar.js

				
					import React, { useState } from "react";
const Searchbar = () => {
const [input, setInput] = useState("");
console.log(input);
return (
<form style={{ marginBottom: "20px" }} onSubmit={(e)=>{}}>
<div className="form-group">
<input
type="text"
className="form-control form-control-lg"
id="searchbar"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Search Title ..."
/>
</div>
</form>
);
};
export default Searchbar;
				
			

Nous avons déclaré une fonction composant nommé Searchbar pour cette fonctionnalité permettant de faire une recherche. input est une variable d’état qui stocke la valeur actuelle de l’input. setInput est une fonction permettant de mettre à jour cette valeur. useState(«  ») initialise input avec une chaîne vide.

style={{ marginBottom: « 20px » }} ajoute un style inline pour donner une marge inférieure de 20 pixels au formulaire. onSubmit={(e) => {}} est un gestionnaire d’événements pour la soumission du formulaire. Actuellement, il ne fait rien.

className= »form-group » applique les styles Bootstrap pour formater le groupe de formulaire. type= »text » définit le type de champ comme texte.

className= »form-control form-control-lg » applique les styles Bootstrap pour un input large.

id= »searchbar » donne un identifiant unique à l’input.

value={input} lie la valeur de l’input à la variable d’état input.

onChange={(e) => setInput(e.target.value)} met à jour l’état input à chaque changement de la valeur de l’input.

placeholder= »Search Title … » affiche un texte d’espace réservé dans l’input lorsqu’il est vide.

En lançant notre application, nous aurons un résultat commende dans le navigateur :

Illustration de l'application Next.js avec présence de barre de recherche

Nous avons déjà mis en place la barre de recherche : lorsque vous saisissez quelque chose, la valeur est actuellement affichée dans la console. Maintenant, nous allons améliorer notre application en intégrant Context API. Cela nous permettra de gérer de manière centralisée les données de recherche et de les rendre accessibles à différents composants de notre application

Implémentation de Context API avec Next.js

Pour centraliser la gestion des données de recherche et les rendre accessibles à différents composants de notre application, nous allons créer un fichier Context.js dans un nouveau dossier appelé lib.components/lib/Context.js

				
					import React, { useState } from "react";
export const Context = React.createContext();
const links = [
{ name: "movie", icon: "film" },
{ name: "serie", icon: "tv" },
{ name: "game", icon: "gamepad" },
];
const ContextProvider = (props) => {
const [state, setState] = useState({
query: "",
links: links,
active: links[0],
});
const setActive = (link) => {
setState({ ...state, active: link });
};
const setQuery = (input) => {
setState({ ...state, query: input });
};
const value = React.useMemo(() => {
return {
state,
setActive,
setQuery,
};
}, [state.query, state.active]);
return <Context.Provider value={value}>{props.children}</Context.Provider>;
};
export default ContextProvider;

				
			

On a cet extrait de code :

				
					const links = [
{ name: "movie", icon: "film" },
{ name: "serie", icon: "tv" },
{ name: "game", icon: "gamepad" },
];
				
			

links est un tableau d’objets qui représente différents types de contenus avec leurs noms et icônes associés.

Ensuite, on aussi celui là :

				
					const ContextProvider = (props) => {
const [state, setState] = useState({
query: "",
links: links,
active: links[0],
});
const setActive = (link) => {
setState({ ...state, active: link });
};
const setQuery = (input) => {
setState({ ...state, query: input });
};
const value = React.useMemo(() => {
return {
state,
setActive,
setQuery,
};
}, [state.query, state.active]);
return <Context.Provider value={value}>{props.children}</Context.Provider>;
};
				
			
  • ContextProvider est un composant fonctionnel qui utilise le hook useState pour définir l’état local (state).
  • state contient trois propriétés :
  • query : Représente la valeur de la recherche.
  • links : Liste des liens définis précédemment.
  • active : Définit le lien actif par défaut comme le premier élément de links.
  • setActive : Met à jour l’état en modifiant la propriété active avec le lien passé en argument.
  • setQuery : Met à jour l’état en modifiant la propriété query avec l’entrée de recherche passée en argument.
  • React.useMemo est utilisé pour optimiser les performances en mémorisant la valeur retournée (ici, l’objet contenant state, setActive, et setQuery).
  • La dépendance [state.query, state.active] indique que value ne sera recalculé que lorsque state.query ou state.active changeront.
  • <Context.Provider> enveloppe les composants enfants (props.children) et fournit le contexte value à tous les composants qui y accèdent.

Ensuite, voyons la modification dans le fichier _app.js pou appliquer ce context dans notre application :

				
					import ContextProvider from "../components/lib/Context";
import "../styles/global.css";
export default function App({ Component, pageProps }) {
return (
<>
<ContextProvider>
<Component {...pageProps} />
</ContextProvider>
</>
);
}
				
			

App est le composant racine de votre application Next.js. Il est responsable de rendre la structure de base de votre application et de gérer la mise en page globale. Il reçoit deux propriétés principales : Component et pageProps.

Component : C’est le composant de page actuellement rendu par Next.js, tel que déterminé par la navigation de l’utilisateur.

pageProps : Ce sont les propriétés passées au composant de page (Component). Elles peuvent inclure des données récupérées lors du rendu côté serveur avec getServerSideProps ou getStaticProps.

<ContextProvider> : Enveloppe le composant Component avec ContextProvider, permettant ainsi à tous les composants rendus de Component de consommer les données et les fonctions partagées à travers le contexte défini dans Context.js.

<Component {…pageProps} /> : Rend le composant de page actuel avec les pageProps passées en tant que propriétés. Cela permet à chaque page de recevoir les données initiales et les paramètres nécessaires à partir du serveur ou d’autres sources.

Maintenant que nous avons configuré le Context API, passons à l’étape suivante, qui consiste à créer le menu de navigation pour notre application.

Gestion des liens actifs dans le menu Next.js

Dans cette section, nous allons explorer comment gérer les liens actifs dans notre menu de navigation. L’objectif est de permettre à un seul élément du menu d’être actif à la fois, offrant ainsi une expérience utilisateur améliorée en indiquant visuellement quelle section est sélectionnée. Nous utiliserons React et le Context API pour centraliser et contrôler l’état actif des liens, assurant ainsi une gestion efficace et cohérente de la navigation dans notre application NextJS.

Pour faire cela, dans le fichier Header.js du dossier components : voyons la modification : components/Header.js

				
					// Header.js
import Link from "next/link";
import React, { useContext } from "react";
import { Context } from "./lib/Context";
const Links = ({ children, query, name }) => {
return query !== "" ? (
<Link href={{ pathname: `/${name}`, query: { query: query } }}>
{children}
</Link>
) : (
<Link href={{ pathname: `/${name}` }}>{children}</Link>
);
};
const Header = () => {
const { setActive, state: { links, active, query } } = useContext(Context);
const handleSetActive = (link) => {
setActive(link === active ? null : link); // Toggle active lin
};
return (
<ul className="nav nav-pills nav-fill" style={{ marginBottom: "20px" }}>
{links.map((link) => (
<li key={link.name} className="nav-item" onClick={() => handleSetActive(link)}>
<Links query={query} name={link.name}>
<span
className={`nav-link ${link === active ? "isActive" : ""}`}
style={{ backgroundColor: link === active ? "purple" : "" }}
>
<i className={`fas fa-${link.icon}`}></i> &nbsp; {link.name}{" "}
</span>
</Links>
</li>
))}
</ul>
);
};
export default Header;
				
			

Importations et dépendances pour une application Next.js

  • Link est importé depuis Next.js pour gérer les liens de navigation.
  • React et useContext sont utilisés pour accéder au contexte de l’application.
  • Context est importé depuis ./lib/Context pour utiliser le contexte personnalisé défini.

Composant Links pour une application Next.js

Ce composant fonctionnel accepte les propriétés children, query, et name. Il génère un lien (Link) conditionnel en fonction de la présence d’une query effectivement nous avons une structure ternaire vérifie si la query est vide (query !== «  »).Si la query n’est pas vide (c’est-à-dire qu’elle a une valeur), le premier bloc est exécuté. Sinon, le deuxième bloc est exécuté .

  • Premier Bloc (Si query n’est pas vide) :

<Link href={{ pathname: /${name}, query: { query: query } }}> le composant Link de Next.js est utilisé pour créer un lien tel que pathname est défini comme /${name}, ce qui construit le chemin de l’URL. query est inclus comme un objet dans l’attribut query, où query: query signifie que la clé dans l’URL sera query avec la valeur fournie par la propriété query.

  • Deuxième Bloc (Si query est vide) :

<Link href={{ pathname: /${name} }}>{children}</Link> encore une fois, utilise le composant Link de l’application Next.js pour créer un lien.Seulement pathname est défini, sans aucune query.

Composant Header avec Next.js

Utilisation du hook useContext pour accéder aux valeurs du contexte défini dans ContextProvider.Définition de links, active, et query à partir de state extrait du contexte. handleSetActive est une fonction qui met à jour l’état actif du lien en fonction du clic. Si le lien cliqué est déjà actif, il est désactivé (null), sinon, il devient actif (link).

Rendu du menu de navigation

Boucle sur links pour générer chaque élément de la liste (li). Chaque élément de liste contient un lien (Links) qui encapsule un élément <span> représentant le lien.Le style du lien actif est modifié dynamiquement .Le fond devient violet (purple) si le lien est actif.

La classe isActive est ajoutée au <span> si le lien est actif, permettant ainsi un style spécifique si nécessaire.

Examinons aussi la modification dans le fichier Layout.js du dossier components : components/Layout.js

				
					import Head from "next/head";
import Header from "./Header";
import Searchbar from "./Searchbar";
const layoutStyle = {
marginTop: 20,
padding: 20,
};
const Layout = ({ children }) => {
return (
<div>
<Head>
<title>Movie Database</title>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />


<link
rel="shortcut icon"
href="https://www.google.com/imgres?imgurl=http%3A%2F%2Fwww.icons101.com%2Ficon_ico%2Fid_78884%2Fmovie.ico&imgrefurl=http%3A%2F%2Fwww.icons101.com%2Ficon%2Fid_78884%2Fsetid_2765%2FIcons_Material_Design_by_Rammist%2Fmovie&tbnid=mCv1dJgE3vsqoM&vet=12ahUKEwjehoyvkuHoAhUS-BoKHRU5B6kQMygJegUIARDvAQ..i&docid=00LXuhA2f4FqTM&w=256&h=256&q=movie%20icon&hl=fr&ved=2ahUKEwjehoyvkuHoAhUS-BoKHRU5B6kQMygJegUIARDvAQ"
/>
</Head>
<div style={layoutStyle}>
<div className="container">
<div className="row">
<div className="col-md-10 offset-1">
<>
<Searchbar />
<Header />
{children}
</>
</div>
</div>
</div>
</div>
</div>
);
};
export default Layout;
				
			

Importations

  • Head est importé depuis NextJS pour gérer les éléments <head> de la page, comme le titre, les méta-informations, et les liens de style.
  • Header est importé depuis./Header pour intégrer le menu de navigation.
  • Searchbar est importé depuis ./Searchbar pour inclure la barre de recherche dans le layout.

Styles de mise en page

layoutStyle est un objet CSS défini pour ajuster le marges (marginTop) et les rembourrages (padding) du contenu du layout.

Composant Layout

Ce composant fonctionnel reçoit children comme une propriété, qui représente le contenu spécifique rendu à l’intérieur du layout.

Utilisation de <Head> pour inclure des métadonnées importantes pour la page, telles que le titre, la vue initiale et les liens vers des feuilles de style externes (bootstrap et font-awesome).

Le contenu est enveloppé dans une structure <div> avec le style défini par layoutStyle.

Une grille Bootstrap est utilisée (container, row, col-md-10 offset-1) pour organiser le contenu de manière responsive dans une largeur maximale de 10 colonnes, centrée avec un décalage de 1 colonne.

Les éléments inclus sont :

  • <Searchbar /> : La barre de recherche pour rechercher des titres de films.
  • <Header /> : Le menu de navigation pour sélectionner entre les catégories de films.
  • {children} : Le contenu spécifique de la page rendue, transmis en tant que propriété.

Tout cela nous donne un résultat comme ceci après avoir lancé notre application :

Visuel montrant l'application Next.js avec menu

Voici le menu de notre application qui s’affiche correctement. Cependant, seul le menu « movie » nous retourne des données de type « movie », tandis que les autres affichent une erreur 404, indiquant qu’ils n’existent pas encore. C’est ce que nous allons aborder dans la prochaine section.

Pour aller plus loin et maîtriser pleinement les compétences abordées dans cet article, nous vous invitons à découvrir les formations d’Alphorm. Formations Alphorm.

Routage de pages avec Next.js

Dans cette section, nous allons créer de nouvelles pages pour les catégories « game » et « serie« , puis nous mettrons en place les routes correspondantes pour ces deux pages. Commençons par créer ces nouvelles pages : pages/serie.js

				
					export default function Serie(){
return(< h2> page serie< /h2>)
}
				
			

Et pour la page game : pages/game.js

				
					export default function Game(){
return(< h2> page game< /h2>)
}
				
			

Et enfin pour la page movie : page/movie .js

Voici nos pages, et nous pouvons y accéder en ajoutant le nom de la page à côté de l’URL principale comme ceci : http://localhost:3000/serie et http://localhost:3000/game . Ces URLs nous donnent respectivement des résultats comme ceci :

Illustration de l'affichage de la page game dans l'application Next.js
Illustration de l'affichage de la page série dans l'application Next.js

Voilà, nos pages s’affichent correctement, mais ce n’est pas notre objectif. Nous voulons les afficher à partir des liens du menu respectif quand on clique sur le lien. Passons à cette étape maintenant en voyant la modification dasn les fichier de page. Page/serie.js

				
					import React from 'react';
import Layout from '../components/Layout';
const Serie = () => {
return (
<Layout>
<main className="my-4">
< h2 className="text-center">Séries disponibles< /h2>
</main>
</Layout>
);
};
export default Serie;
				
			

Nous avons juste enveloppe notre page dans le composant Layout et pareil pour la page game et movie .

Pour la page game : pages/game.js

				
					import React from 'react';
import Layout from '../components/Layout';
const Game = () => {
return (
<Layout>
<main className="my-4">
< h2 className="text-center">Jeux disponibles< /h2>
</main>
</Layout>
);
};
export default Game;
				
			

Après avoir implémenté cela, nous avons correctement configuré les liens pour toutes les pages et cela nous donne un résultat comme ceci :

Visuel montrant page série avec route établie dans l'application Next.js

Quand la page « serie » est cliquée, elle affiche bien son contenu. Donc, la route pour cette page est bien établie.

Utilisation de getInitialProps dans Next.js

Dans cette section, nous explorerons getInitialProps, une méthode essentielle de NextJS qui permet de précharger des données côté serveur avant de rendre une page. Cette fonctionnalité est cruciale pour améliorer les performances et gérer efficacement le chargement initial des données dans une application Next.js.

Modifions maintenant le fichier index.js pour tester cette fonctionnalité de nextJS.

				
					import fetch from "node-fetch";
import List from "../components/List";
function Home({ results, search }) {
console.log({search});
return (
<>
<List results={results} />
</>
);
}
Home.getInitialProps = async ({
req,
res,
match,
history,
location,
...ctx
}) => {
const search = ctx?.query?.query ?? "";
const response = await fetch(
`http://www.omdbapi.com/?apikey=158fe49&s=${search}type=serie`
);
const data = await response.json();
const results = data.Search ;
return {
props:{results, search },
};
};
export default Home;
				
			

Nous avons le composant Home qui reçoit des props results et search et les passe au composant List pour l’affichage.

Nous avons aussi utilisé la méthode statistique getInitialProps   pour initialiser les props avant que le composant ne soit rendu.

La ligne de code suivante nous permet d’extraire la chaîne de recherche (query) des paramètres de la requête URL (via le contexte ctx).

				
					const search = ctx?.query?.query ?? "";
				
			

Les lignes suivantes effectuent une requête à l’API OMDb pour rechercher des séries correspondant à la chaîne de recherche et convertissent la réponse en format JSON.

				
					const response = await fetch(
`http://www.omdbapi.com/?apikey=158fe49&s=${search}type=serie`
);
const data = await response.json();
				
			

Et par la suite la ligne de code suivant extrait les résultats de la recherche de la réponse JSON :

				
					const results = data.Search;
				
			

Enfin, les résultats de la recherche ainsi que la chaîne de recherche seront renvoyés sous forme de props au composant Home.

Nous examinerons l’effet de cette fonctionnalité dans la prochaine section, qui traite des résultats de la requête API. Cette section complétera notre compréhension en démontrant comment cette fonctionnalité contribue à des changements notables.

Résultats de requête API avec Next.js

Nous allons interroger l’API de l’OMDB pour effectuer la requête. Lorsqu’un utilisateur tape un titre de film dans la barre de recherche, l’application affichera les résultats correspondants.

Avant de continuer, une petite précision : la page des films est la page d’accueil de notre application. Par conséquent, le contenu du fichier index.js doit être identique à celui de movie.js. Faisons cela maintenant. Pages/index.js

				
					import fetch from "node-fetch";
import List from "../components/List";
function Movie({ results }) {
return (
<>
<List results={results} />
</>
);
}
Movie.getInitialProps = async ({
req,
res,
match,
history,
location,
...ctx
}) => {
const search = ctx?.query?.query ?? "";
// Get external data from the file system, API, DB, etc.
const response = await fetch(
`http://www.omdbapi.com/?apikey=158fe49&s=${search}type=serie`
);
const data = (await response.json()) ?? [];
const results = data.Search ?? [];
return {
results,
};
};
export default Movie;

				
			

pages/movie.js

				
					import fetch from "node-fetch";
import List from "../components/List";
function Movie({ results }) {
return (
<>
<List results={results} />
</>
);
}
Movie.getInitialProps = async ({
req,
res,
match,
history,
location,
...ctx
}) => {
const search = ctx?.query?.query ?? "";
// Get external data from the file system, API, DB, etc.
const response = await fetch(
`http://www.omdbapi.com/?apikey=158fe49&s=${search}&type=movie`
);
const data = (await response.json()) ?? [];
const results = data.Search ?? [];
return {
results,
};
};
export default Movie;
				
			

Voici, la méthode statique getInitialProps est toujours implémentée et le contenu est le même que dans la section précédente. Cependant, nous allons désactiver le comportement par défaut de notre page en ajoutant cette ligne de code dans composant setResults du fichier Searchbar.js.

				
					e.preventDefault();
Router.push({ pathname: "/movie", query: { query: query } });
				
			

e.preventDefault(): Cette ligne empêche le comportement par défaut de l’événement. Dans le cas d’un formulaire, cela empêche le formulaire de se soumettre automatiquement et de recharger la page

Router.push({ pathname: “/movie”, query: { query: query } }); :

Router.push est une méthode de NextJS utilisée pour naviguer vers une autre page de l’application. elle permet de gérer la navigation côté client sans recharger la page entière.

pathname : Spécifie le chemin de la page vers laquelle l’utilisateur doit être redirigé. Dans ce cas, c’est la page « /movie ».

query : Un objet contenant les paramètres de la requête. Ici, il contient une clé query dont la valeur est la variable query. Cela signifie que l’URL résultante inclura ce paramètre de requête

Donc, le fichier Searchbar.js est devenu comme ceci : components/Searchbar.js

				
					import Router from "next/router";
import React from "react";
import { Context } from "./lib/Context";
const Searchbar = () => {
const [input, setInput] = React.useState("");
const {
setQuery,
state: { query },
} = React.useContext(Context);
const setResults = (e) => {
e.preventDefault();
Router.push({ pathname: "/movie", query: { query: query } });
};
return (
<form style={{ marginBottom: "20px" }} onSubmit={(e) => setResults(e)}>
<div class="form-group">
<input
type="text"
className="form-control form-control-lg"
id="searchbar"
defaultValue={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search Title ..."
/>
</div>
</form>
);
};
export default Searchbar;
				
			

Testons maintenant l’effet de la méthode statique getInitialProps et de notre API en démarrant l’application avec la commande habituelle npm run dev.

Capture d'écran du résultat de la requête API dans une application Next.js

Voilà, après avoir saisi le titre d’un film de notre choix, le résultat correspondant s’affiche correctement. L’API fournit les données disponibles et la méthode restitue bien ces données. Améliorons notre application en complétant les requêtes API dans les pages série et jeu dans la dernière section du chapitre.

Récupération de données SSR avec Next.js

Étant donné que la requête API est la même pour toutes les pages, le composant Movie est réutilisable dans les autres pages en ajustant simplement le nom et l’URL de la page. Profitons des fonctionnalités fournies par Next.js pour récupérer les données.

Pages/game.js

				
					import fetch from "node-fetch";
import List from "../components/List";
function Game({ results }) {
return (
<>
<List results={results} />
</>
);
}
Game.getInitialProps = async ({
req,
res,
match,
history,
location,
...ctx
}) => {
const search = ctx?.query?.query ?? "";
// Get external data from the file system, API, DB, etc.
const response = await fetch(
`http://www.omdbapi.com/?apikey=158fe49&s=${search}&type=game`
);
const data = (await response.json()) ?? [];
const results = data.Search ?? [];
// The value of the `props` key will be
//  passed to the `Home` component
return {
results,
};
};
export default Game;
				
			

Voilà, nous avons simplement copié le contenu de la page index dans ce fichier, en modifiant uniquement le nom et le type comme « game » dans la requête de l’API. Nous ferons de même pour la page série.pages/serie.js

				
					import fetch from "node-fetch";
import List from "../components/List";
function Serie({ results }) {
console.log({ results });
return (
<>
<List results={results} />
</>
);
}
Serie.getInitialProps = async ({
req,
res,
match,
history,
location,
...ctx
}) => {
const search = ctx?.query?.query ?? "";
// Get external data from the file system, API, DB, etc.
const response = await fetch(
`http://www.omdbapi.com/?apikey=158fe49&s=${search}&type=series`
);
const data = (await response.json()) ?? [];
const results = data.Search ?? [];
// The value of the `props` key will be
//  passed to the `Home` component
return {
results,
};
};
export default Serie;
				
			

Enfin , voyons la modification dansle fichier _app.js pour l’adapter avec nos pages.

Pages/_app.js

				
					import Layout from "../components/Layout";
import ContextProvider from "../components/lib/Context";
import "./styles.css";
export default function App({ Component, pageProps }) {
return (
<>
<h1>Movie Database App</h1>
<ContextProvider>
<Layout>
<Component {...pageProps} />
</Layout>
</ContextProvider>
</>
);
}
				
			

App : Définit une fonction composant App qui est exportée par défaut.

{ Component, pageProps } : Destructure les props passées au composant App. Component représente le composant de page actuellement rendu par NextJS, et pageProps contient les props initiales pour ce composant.

<h1>Movie Database App</h1> : Un titre de niveau 1 affichant « Movie Database App » qui est rendu sur toutes les pages de l’application.

<ContextProvider> : Englobe le contenu de l’application avec le contexte fourni par ContextProvider. Cela permet de gérer et de partager des données ou des états entre les différents composants de l’application.

<Layout> : Encadre le composant Component avec un Layout.

<Component {…pageProps} /> : Rend le composant de page actuel (Component) avec les props initiales (pageProps). Cela permet de transmettre les données nécessaires au rendu de la page à partir du serveur ou d’autres sources.

Voyons aussi la modification dans le fichier Card.js : components/Card.js

				
					const placeholderImg =
"https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcQDsfRCwQvpsd4O5b6IK9evG9H1PTxZLoI6ew5iVnlz3ftQjMBQ";
const Card = ({ Title, Poster, Year, imdbID }) => {
return (
<div class="card" style={{ marginBottom: "10px" }}>
<div class="card-body d-flex justify-content-between">
<div class="d-flex align-items-start">
<a href="{`https://www.imdb.com/title/${imdbID}`}" target="_self" style="{{" color:="" &quot;#9b59b6&quot;="" }}="" data-wpel-link="internal" rel="follow noopener noreferrer">
<img
className="mr-2 rounded"
src={Poster === "N/A" ? placeholderImg : Poster}
alt="placeholder"
width="100"
height="130"
/>
<span class="title" style={{ fontSize: "20px" }}>
{Title}
</span>
</a>
</div>
<p style={{ fontSize: "20px" }}>{Year}</p>
</div>
</div>
);
};
export default Card;
				
			

const placeholder : Cette constante définit l’URL d’une image de remplacement utilisée lorsque l’image du film (Poster) n’est pas disponible (Poster === « N/A »).

Props du composant : Le composant Card reçoit plusieurs props : Title, Poster, Year, et imdbID qui les informations disponibles pour chaque film.

Image du film (Poster) : Utilisation conditionnelle de l’URL de l’affiche du film (Poster) ou de placeholderImg si l’affiche n’est pas disponible (Poster === « N/A »).

Lien IMDb : Le titre du film (Title) est un lien vers la page IMDb correspondante, ouvrant dans un nouvel onglet (target= »_blank »).

Styling : Utilisation de styles en ligne pour ajuster la couleur, la taille de police, et d’autres propriétés visuelles des éléments HTML rendus.

Lançons notre application maintenant pour la tester s’elle fonctionne comme il faut.

 

Capture d'écran du test final de l'application Next.js

Parfait, notre application répond pleinement à nos besoins. Nous avons conclu ce chapitre avec succès. Bravo ! Vous êtes désormais capable de développer une application universelle avec Next.js.

Conclusion

En suivant ce guide, vous avez appris à créer une application Next.js pour la recherche de films, en intégrant des fonctionnalités essentielles et avancées. De la récupération de la clé d’API à l’affichage des résultats, en passant par l’initialisation du projet et la mise en place des composants, chaque étape a été optimisée pour offrir une performance maximale. Cette application Next.js n’est pas seulement un outil puissant pour rechercher des films, mais aussi un excellent moyen d’améliorer vos compétences en développement web. Continuez à explorer les possibilités offertes par Next.js et l’API OMDB pour créer des applications encore plus sophistiquées et performantes

ÉTIQUETÉ : NextJS
Partager cet article
Par L'Équipe Alphorm Démocratiser la Connaissance Informatique pour Tous !
Suivre :
L'Équipe Alphorm, c'est la démocratisation de la connaissance informatique. Passionnés et dévoués, nous sommes là pour vous guider vers le succès en rendant la technologie accessible à tous. Rejoignez notre aventure d'apprentissage et de partage. Avec nous, le savoir IT devient une ressource inspirante et ouverte à tous dans un monde numérique en constante évolution.