8 minutes

Tutoriel React-Redux avec Un Projet Simple

Dernière mise à jour: 1 juillet, 2022

La gestion de l'état en reactjs n'est pas très difficile lorsque vos projets sont suffisamment petits. Au fur et à mesure que vos projets grandissent, vous vous rendez compte que le fait de lever l'état vers le parent ou de passer l'état vers l'enfant ne fait que rendre votre code plus difficile à lire et à comprendre. Redux à ce stade résout nos problèmes, récemment avec encore moins de code passe-partout. Commençons!

Dans cet article, nous allons créer une application e-commerce simple en utilisant react et react-redux. Je crois que ce sera plus facile à comprendre de cette façon. J'expliquerai également les morceaux de code au fur et à mesure que nous les parcourrons. Si vous vous sentez coincé quelque part et si vous voyez une erreur, vous pouvez simplement visiter le repository.

§Création de Notre Projet

Nous allons commencer notre projet avec une simple commande npx.

npx create-react-app react-redux-project

Lorsque la configuration du projet est terminée, nous pouvons continuer et installer les dépendances que nous utiliserons. Nous devons bien sûr installer react-redux et gérer le routage avec react-router-dom.

npm i react-router-dom react-redux

§Nettoyez Le Projet de Démarrage

Tout d'abord, supprimons le logo car nous ne l'utiliserons nulle part dans notre projet. Nous pouvons supprimer App.css, App.js et App.test.js ainsi que nous créerons nos composants et pages dans des dossiers et fichiers que nous créerons bientôt. Maintenant, nous pouvons tout supprimer sauf le "wrapper" div dans le fichier index.js. Lorsque nous aurons terminé, notre fichier index.js devrait ressembler à ceci :

import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import reportWebVitals from './reportWebVitals'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <div>Test</div> </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();

Si tout s'est bien passé jusqu'à présent, lorsque vous exécutez la commande bash npm start , vous devriez maintenant voir le Test sur votre écran. Nous sommes maintenant prêts à écrire notre code.

§Détails et Planification du Projet

Je pense qu'un site Web e-commerce typique est un bon exemple. Nous pourrons voir à quel point il est facile de gérer l'état en utilisant la fonctionnalité redux typique. Dans notre page d'accueil, nous allons conserver 9 éléments dans une disposition en grille (grid). Chaque article aura un bouton qui nous permettra d'ajouter l'article sélectionné dans notre panier. Nous pourrons voir la quantité des articles que nous avons dans notre panier sur la barre de navigation. Nous garderons le compte avec un simple badge dans le lien vers la page du panier.

Une note personnelle, avant les changements récents que redux passait par le code passe-partout pour que les choses démarrent, c'était tout simplement trop de travail. Personnellement, j'éviterais d'utiliser redux autant que possible. Bien sûr, c'est toujours à vous de décider si vous en avez besoin ou non.

Comme notre projet est petit et simple, je ne voulais pas le compliquer en ajoutant une bibliothèque CSS. Nous créerons nos fichiers CSS dans les dossiers que nous créerons pour chaque page et composant. De cette façon, notre code sera plus facile à lire et à comprendre.

Dans le dossier src, créez les dossiers page et components. Nous allons créer nos pages et composants dans ces dossiers.

§Création du Store

Vous pouvez considérer le magasin comme l'endroit où tout l'état et la logique pour définir notre état existent. Garder le magasin séparé comme cela nous permet d'y accéder à partir de n'importe quel composant que nous voulons. La seule façon de changer l'état à l'intérieur est d'y envoyer une action.

Créez un dossier appelé app dans le dossier src. Dans le dossier, créez le fichier store.js. Le code de ce fichier est très simple comme vous pouvez le voir :

import { configureStore } from '@reduxjs/toolkit' import shopReducer from '../slice/shopSlice' export default configureStore({ reducer: { shop: shopReducer }, })

Comme nous n'avons pas encore notre "reducer" et notre "slice", vous verrez une erreur après la création du "store". Ne vous inquiétez pas pour l'instant car nous allons bientôt le créer.

Nous avons créé le store pour notre boutique. Maintenant, nous pouvons gérer toute la logique nécessaire pour faire bouger les choses.

§Création de La Tranche

Créer une slice avec la redux toolkit est très simple. Ce n'était pas aussi simple que cela auparavant et je pense que c'était la principale raison pour laquelle les gens (moi y compris) évitaient d'utiliser redux. Nous avons dû créer des actions, des types d'actions, etc. C'était tout simplement trop ennuyeux.

Nous utiliserons la fonction createSlice pour, eh bien, créer une slice. Cette fonction accepte un état initial, des fonctions de reducer à envoyer et un nom pour notre slice. C'est ça! La Redux toolkit s'occupe de générer des créateurs et des types d'action.

Dans le dossier src, nous allons créer un nouveau fichier nommé shopSlice. Je pense que le code est très explicite, mais j'ai également ajouté des commentaires pour faciliter la compréhension. Le code complet de ce fichier est ici :

import { createSlice } from '@reduxjs/toolkit' // this object will be used as our initial state. const state = { // Notre panier est initialement vide cart: [], // Notre tableau que nous allons montrer dans notre page d'accueil // Nous allons simplement utiliser les couleurs comme articles à vendre items: [ { id: 1, name: "cyan", price: 10.99}, { id: 2, name: "teal", price: 12.99}, { id: 3, name: "yellow", price: 14.99}, { id: 4, name: "lime", price: 12.99}, { id: 5, name: "pink", price: 6.99}, { id: 6, name: "purple", price: 2.99}, { id: 7, name: "turquoise", price: 4.99}, { id: 8, name: "red", price: 18.99}, { id: 9, name: "gold", price: 20.99}, ] } export const shopSlice = createSlice({ name: 'shop', initialState: state, reducers: { // l'action est le moyen d'envoyer des données avec nos dépêches à stocker pour changer notre état // action.payload dans notre cas n'aura que nos identifiants d'articles, de cette façon nous pourrons // définir notre état addItemToCart: (state, action) => { // Vérifions d'abord si l'article existe dans le panier let item = state.cart.filter(x => x.id === action.payload)[0] // Si nous avons un article dans le panier, nous augmenterons simplement sa quantité if(item) { let newQuantity = item.quantity + 1; // Nous utilisons l'opérateur de propagation pour créer notre nouvel article avec une quantité incrémentée item = {...item, quantity: newQuantity} let i = state.cart.findIndex(i => i.id === action.payload) // Nous utilisons la méthode de slice pour découper les pièces jusqu'à et après l'article dont nous sommes // modifier la quantité, // Encore une fois en utilisant l'opérateur de propagation pour créer notre nouveau panier state.cart = [...state.cart.slice(0,i), item,...state.cart.slice(i+1)] } // Si nous n'avons pas l'article dans notre panier else { // Nous devons obtenir l'article et ajouter une nouvelle clé, quantité let i = state.items.filter(x => x.id === action.payload)[0]; i = {...i, quantity: 1 }; // Ajoutez-le enfin à notre panier state.cart = [...state.cart, i] } }, removeFromCart: (state, action) => { // Filtrer l'élément state.cart = state.cart.filter(i => i.id !== action.payload) }, // Logique de décrémentation dont nous aurons besoin dans notre panier decrementQuantity: (state, action) => { // Parcourez le tableau à l'aide de la méthode forEach et décrémentez la quantité de l'élément // Comme vous pouvez le voir, nous n'avons pas eu besoin de créer un nouveau tableau de manière non mutante, comme nous l'avons fait // pour la méthode d'incrémentation. Bien sûr, il est possible d'utiliser une méthode comme celle-ci ici // pour ajouter un article à notre panier. state.cart.forEach(i => { if(i.id === action.payload) { if(i.quantity > 1) { i.quantity -= 1; }else{ // s'il n'y en a qu'un, supprimez l'élément state.cart = state.cart.filter(i => i.id !== action.payload) } } }) }, clearCart: (state) => { // Vider le panier state.cart = []; } }, }) // Des sélecteurs vont être utilisés pour atteindre l'état. On peut les considérer comme des getters export const selectCart = (state) => state.shop.cart; export const selectItems = (state) => state.shop.items; // Enfin nous exportons toutes les actions à dispatcher export const { addItemToCart, removeFromCart, decrementQuantity , clearCart } = shopSlice.actions // Nous exportons notre reducer pour être utilisé en store export default shopSlice.reducer

C'était tout le code nécessaire pour exécuter notre simple site Web de commerce électronique. Comme je l'ai également écrit dans les commentaires ci-dessus, vous pouvez modifier la logique de manière mutante. Cette méthode est désormais totalement sûre, car redux s'occupe de la logique sous le capot pour nous.

§Provider Wrapper

Dans notre fichier index.js, nous devons importer le fournisseur à partir de react redux. Envelopper notre application avec le Provider rend le store visible et nous permet d'interagir avec le store. Le code est également très simple, avec ce dernier ajout notre index.js ressemble à ceci :

import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import reportWebVitals from './reportWebVitals'; import store from './app/store'; import { Provider } from 'react-redux'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <Provider store={store}> <div>Test</div> </Provider> </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();

§Commencez à Construire Les Itinéraires

Nous allons gérer le routage à l'aide du package react-router-dom. Ce package nous permettra d'utiliser une mise en page wrapper pour nos pages et de cette façon, vous verrez que le lien vers nos propres pages aura l'air impeccable. Nous ne rechargerons pas toute la page mais seulement les parties qui sont différentes. Je ne vais pas approfondir le routage, je vous suggère de lire la documentation ici si vous voulez en savoir plus.

Nous pouvons créer des pages de base et notre barre de navigation afin de tester la fonctionnalité de notre routeur. Continuons et créons les fichiers Cart.jsx et Home.jsx dans le dossier pages. Nous n'avons besoin de rien de cool ici dans ces pages jusqu'à présent. Vous pouvez simplement créer un composant de réaction ici. Ces pages seront ensuite utilisées pour importer dans tous les composants que nous utiliserons dans nos pages. La séparation de nos pages et de nos composants de cette façon les rend plus faciles à comprendre plus tard et je pense que c'est juste une bonne pratique.

Si vous avez créé les pages, nous pouvons maintenant créer notre dossier navbar. Dans ce dossier, nous allons créer un fichier Navbar.jsx et un fichier Navbar.css. Nous allons importer deux éléments (je ne sais pas vraiment comment les appeler), Lİnk et Outlet. Link remplacera les balises a pour les liens internes et nous fournira une expérience de routage sans faille. De cette façon, nous ne rechargerons pas les pièces qui restent les mêmes comme notre barre de navigation ici. Le Outlet sera utilisé pour déterminer où les composants enveloppés apparaîtront à l'intérieur de notre composant Navbar. Voir la fonctionnalité décrite en action nous aidera à mieux comprendre.

Dans notre Navbar, je voulais ajouter un simple badge comme un compteur qui nous montrera la quantité d'articles dans notre panier. Pour ce faire, nous allons utiliser un sélecteur. Nous allons sélectionner le panier et calculer la quantité d'articles dans un crochet useEffect. Nous pourrions créer cette fonctionnalité et cette logique en utilisant redux. Je viens de le faire de cette façon. Si vous voulez créer un défi simple pour vous-même et tester vos connaissances sur redux, n'hésitez pas à implémenter la logique en utilisant redux.

Afin de rendre ce code plus facile à comprendre, j'ai ajouté quelques commentaires ci-dessous. Le code fini de notre barre de navigation ressemblera à ceci :

import React, { useState, useEffect } from 'react' import { Outlet, Link } from 'react-router-dom' import './Navbar.css' import { useSelector } from 'react-redux'; import { selectCart } from '../../slice/shopSlice'; const Navbar = () => { const cart = useSelector(selectCart) const [cartQuantity, setCartQuantity] = useState(0); useEffect(() => { if(cart.length >= 0){ let n = 0; cart.map(x => n += x.quantity) setCartQuantity(n) } }, [cart, cartQuantity]) return (<> <div className='navbar-wrapper'> <Link className='nav-link' to="/">Home</Link> <Link className='nav-link' to="/cart">Cart <span className='length-badge'>{cartQuantity}</span></Link> </div> {/* Using Outlet here will make the components that we are going to wrap with our Navbar appear here. */} <Outlet /> </>) } export default Navbar

Nous pouvons maintenant styliser notre barre de navigation. Bien sûr, n'hésitez pas à utiliser votre propre style si vous n'aimez pas le mien, je dois admettre que je ne suis pas le meilleur concepteur de sites Web. Maintenant, dans le fichier Navbar.css, ajoutez ce code ci-dessous :

.navbar-wrapper { height: 100px; text-align: end; padding: 20px 40px; font-size: 1.4rem; position: fixed; left:0; right: 0; font-weight: bold; background: linear-gradient(45deg, rgba(255,255,255,0), rgba(255, 255, 255, .1)); z-index: 100; } .nav-link { color: rgb(96, 1, 1); text-decoration: none; margin-right: 40px; font-size: 1.8rem; transition: 0.2s; } .nav-link:hover { color: rgb(130, 62, 62); } .length-badge { font-size: 0.9rem; }

Nous n'aurons plus besoin de modifier notre barre de navigation, nous pouvons donc également gérer le fichier index.js et tester notre routeur. Le code complet de notre routeur se trouve ci-dessous :

import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import reportWebVitals from './reportWebVitals'; import store from './app/store'; import { Provider } from 'react-redux'; import { BrowserRouter, Routes, Route } from 'react-router-dom'; import Navbar from './components/navbar/Navbar'; import Home from './pages/Home'; import Cart from './pages/Cart'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <Provider store={store}> <BrowserRouter> <Routes> <Route path="/" element={<Navbar/>}> <Route index element={<Home />} /> <Route path="cart" element={<Cart/>} /> </Route> </Routes> </BrowserRouter> </Provider> </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();

Le code complet de index.css est également ici :

* { box-sizing: border-box; padding: 0; margin: 0; } img { width: 100%; } body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; min-height: 100vh; } code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; }

Si tout s'est bien passé jusqu'à présent, vous devriez pouvoir visiter les 2 pages que nous avons crées en utilisant les liens de notre barre de navigation.

§Nous Pouvons Maintenant Créer Le Reste de Nos Composants

Je pense qu'il serait plus logique de commencer à créer les composants que nous utiliserons dans notre page d'accueil. Nous avons besoin d'une bannière qui indique à l'utilisateur sur quelle page il se trouve actuellement, et nous devons montrer tous les éléments disponibles à l'utilisateur. Nous pouvons utiliser cette bannière à l'intérieur de la page du panier afin de créer un composant qui prend le texte comme accessoire à afficher. Commençons par créer la bannière.

Dans le dossier components, créez un dossier banner. Dans ce dossier, nous allons créer les fichiers Banner.jsx et Banner.css. Ces deux fichiers sont très simples et faciles à comprendre :

import React from 'react' import './Banner.css' const Banner = ({text}) => { return ( <div className='banner-wrapper'> <p className='banner-text'>{text}</p> </div> ) } export default Banner .banner-wrapper { height: 300px; background: teal; } .banner-text { font-size: calc(32px + 2vw); margin-left: 140px; padding-top: 110px; color:rgb(238, 71, 71); text-decoration: under; font-weight: bold; text-shadow: 2px 2px 2px rgb(1, 34, 70); }

Maintenant que cela est pris en charge, nous pouvons penser à un composant qui affichera nos articles. Pour cette fonctionnalité, nous allons en fait avoir besoin de deux composants. Un composant qui recevra les éléments et un autre composant que nous pouvons rendre pour chaque élément du tableau d'éléments.

Créons le dossier items à l'intérieur des composants et à l'intérieur du dossier, nous allons créer les fichiers Items.jsx et Items.css. Ici aussi, nous n'avons besoin que de la fonctionnalité de sélection. Nous recevrons nos articles dans ce composant. Le code complet de ce composant est le suivant :

import React from 'react' import { useSelector } from 'react-redux' import { selectItems } from '../../slice/shopSlice' import './Items.css' const Items = () => { const items = useSelector(selectItems); return ( <div className='items-container'> </div> ) } export default Items

Et le code du fichier CSS pour rendre ce composant moins horrible à regarder :

.items-container { display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: center; max-width: 960px; margin: -40px auto 260px; background: rgb(221, 254, 255); box-shadow: 2px 2px 32px 2px rgb(0, 0, 0); padding: 40px; border-radius: 10px; }

Simple, non ?

Maintenant que nous recevons nos articles, nous pouvons les mapper (map()) à l'aide d'un autre composant. Nous n'avons pas besoin d'utiliser un composant car c'est une opération très simple mais nous aimons garder notre code propre. Allez-y et créez le dossier item dans le dossier compoenents. Dans ce dossier, créez les fichiers Item.jsx et Item.css. Dans notre fichier Item.jsx, nous allons créer un composant dans lequel nous prendrons les informations sur l'article comme accessoire et afficherons nos articles. Nous ajouterons un bouton pour chaque article afin d'ajouter l'article choisi à notre panier. Avec ce bouton, nous enverrons une action et manipulerons notre panier de cette façon.

Le code complet de notre fichier Item.jsx est le suivant :

import React from 'react' import './Item.css' import { useDispatch } from 'react-redux' import { addItemToCart } from '../../slice/shopSlice' const Item = ({ name, id, price}) => { const dispatch = useDispatch(); return ( <div className='item-cart'> <div className='color-box' style={{background: name}}></div> <p className='p-name'>{name}</p> <p className='p-price'>{price} $</p> <button onClick={() => dispatch(addItemToCart(id))} className='add-button'>Add to Cart</button> </div> ) } export default Item

Maintenant, stylisons un peu notre composant, le code du fichier Item.css est également ici :

.item-cart { max-width: 260px; min-width: 200px; margin: 20px; box-shadow: 2px 2px 21px 2px rgb(180, 241, 180); padding: 20px; border-radius: 10px; background: white; } .color-box { padding: 80px; } .p-name { margin-top: 20px; margin-bottom: 10px; color: rgb(16, 57, 99); text-transform: capitalize; font-size: 1.45rem; } .p-price { color: rgb(116, 0, 73); } .add-button { margin-top: 10px; border: none; width: 100%; padding: 10px; background-color:hsl(160, 100%, 75%); border-radius: 15px; transition: .2s; font-size: 1.2rem; box-shadow: 2px 2px 2px 2px teal; } .add-button:hover { background-color: hsl(160, 100%, 24%); color: white; cursor: pointer; } .add-button:active { box-shadow: none; }

C'était tous les composants dont nous avions besoin pour notre page d'accueil. Donc, le code complet de notre composant est ici :

import React from 'react' import Banner from '../components/banner/Banner' import Items from '../components/items/Items' const Home = () => { return (<> <Banner text="Home"/> <Items /> </>) } export default Home

§Manipulation du Chariot

Nous pouvons également utiliser le composant de bannière que nous créons pour cette page. C'est l'une des raisons pour lesquelles j'aime travailler avec des composants et les frameworks basés sur des composants sont les plus populaires jusqu'à présent.

Pour cette raison, nous allons nous concentrer sur la logique du panier, en affichant et en supprimant des articles dans notre panier. Nous nous occuperons de tout dans le composant. Dans le dossier components, créez un dossier nommé inCart. Dans ce dossier, créez les fichiers InCart.jsx et InCart.css. Ce composant est le plus important à ce jour.

If there is no item in the cart we will show the user a text and a link so they can easily go back to shopping. Otherwise we are going to show our items as list elements. Add buttons for incrementing, decrementing the quantity of an item in the cart. We will add a button to remove an item directly and also a button for clearing the cart by removing all the items. The full code for this component is as follows:

import React from 'react' import './InCart.css' import { useDispatch, useSelector } from 'react-redux' import { removeFromCart, clearCart, selectCart, addItemToCart, decrementQuantity } from '../../slice/shopSlice' import { Link } from 'react-router-dom' const ItemsInCart = () => { const dispatch = useDispatch(); const cart = useSelector(selectCart) return ( <div className='cart-items-container'> {cart.length === 0 ? <p className='empty-cart-text'>Your cart appears to be empty,<Link to="/" className='home-button'> wanna see the items ?</Link></p> : <> <ul className='cart-items-ul'> {cart.map(i => <li className='cart-item-li' key={i.id}> {i.name} <div className='right-align'> <button onClick={() => dispatch(addItemToCart(i.id))}>+</button> {i.quantity} <button onClick={() => dispatch(decrementQuantity(i.id))}>-</button><button onClick={() => dispatch(removeFromCart(i.id))} className='remove-button'>Remove</button> </div> </li>)} </ul> <button onClick={() => dispatch(clearCart())} className='clear-cart-button'>Clear Cart</button> </> } </div> ) } export default ItemsInCart

Le code était assez explicite, c'est pourquoi je n'ai ajouté aucun commentaire. Si vous avez l'impression qu'il y a une partie que vous ne comprenez pas entièrement. Changez simplement le code et écrivez le vôtre, jouez un peu avec. Jusqu'à ce que vous puissiez vous expliquer clairement le code à voix haute. C'est une méthode que j'utilise souvent lorsque j'essaie de comprendre si j'ai réellement appris quelque chose ou non.

Maintenant que nous avons toute la logique, ajoutons un peu de style. Voici le code CSS que j'ai écrit pour ce composant :

.cart-items-container { display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: center; max-width: 960px; min-height: 600px; margin: -40px auto 260px; background: rgb(221, 254, 255); box-shadow: 2px 2px 32px 2px rgb(0, 0, 0); padding: 40px; border-radius: 10px; } .empty-cart-text { font-size: 1.4rem; color: rgb(0, 0, 0); } .home-button { text-decoration: none; color: rgb(121, 121, 255); } .cart-items-ul { color: rgb(0, 0, 0); list-style: none; width: 100%; } .cart-item-li { font-size: 1.4rem; text-transform: capitalize; border-bottom: 1px solid rgb(0, 0, 0); padding: 20px; width: 100%; display: flex; justify-content: space-between; align-items: center; } .right-align > button { padding: 10px 20px; min-width: 70px; margin: 0 20px; border-radius: 5px; font-size: 1.9rem; } .right-align > button:hover { cursor: pointer; } .remove-button { background-color: red; border: 2px solid white; } .clear-cart-button { padding: 10px 20px; min-width: 70px; margin: 0 20px; font-size: 1.9rem; margin-top: 60px; } .clear-cart-button:hover { cursor: pointer; }

C'était facile, non ? Et maintenant, notre fichier Cart.jsx ressemble à ceci :

import React from 'react' import Banner from '../components/banner/Banner' import ItemsInCart from '../components/inCart/InCart' const Cart = () => { return (<> <Banner text="Cart"/> <ItemsInCart /> </>) } export default Cart

§Conclusion

Nous avons maintenant une application finalisée devant nous. La redux n'est certainement pas si difficile en ce moment. Nous avons pu utiliser un "store" pour conserver notre état et modifier la logique. Pensez simplement à la façon dont vous géreriez les changements d'état et passeriez les accessoires Tellement ennuyeux !

J'espère que vous avez apprécié la lecture et la création de ce projet vous-même. J'espère que vous avez appris quelque chose de ce post et que vous vous êtes amusé en le faisant, je sais que je l'ai fait.

Ne vous arrêtez pas là, ajoutez une fonctionnalité de paiement, créez vos articles, créez un backend, connectez-vous à une base de données etc. faites-en le vôtre ! Continuez à apprendre !

Ilker Akbiyik