Skip to content
React

React

Nouveau projet avec Vide

npm create vite@latest [project_name] -- --template react

  • main.jsx: point d’entrée de l’application
  • App.jsx: composant root

Définition d’un composant

// composant de base React
const App = () => (
  <div>
    <p>Hello world</p>
  </div>
);

export default App;

Tout code JS dans les accolades sera évalué et le résultat de cette évaluation sera intégré à l’emplacement définis dans le code HTML.

Pour rendre des expressions JS dans le composant: {} :

const App = () => {
  const now = new Date();
  const a = 10;
  const b = 20;

  return (
    <div>
      <p>Hello world, it is {now.toString()}</p>
      <p>
        {a} plus {b} is {a + b}
      </p>
    </div>
  );
};

Rendre un composant

React est conçu avec une philosophie ou l’application est découpée en plusieurs composants spécialisé et réutilisable.

Le composant App est le composant racine placé en haut de l’arborescence de l’application.

Pour afficher un composant, il suffit de le déclarer dans un autre composant

// définition du composant enfant
const Hello = () => {
  return (
    <div>
      <p>Hello world</p>
    </div>
  );
};

// composant parent
const App = () => {
  return (
    <div>
      <h1>Greetings</h1>

      <Hello />
    </div>
  );
};

Communication parent -> enfant

Pour transmettre des données du parent vert l’enfant, on les passes comme paramètres du composant enfant.

Le composant enfant reçoit un objet contenant les données.

// composant enfant
const Hello = (props) => {
  return (
    <div>
      <p>Hello {props.name}</p>
    </div>
  );
};

// comnposant parent
const App = () => {
  return (
    <div>
      <h1>Greetings</h1>

      <Hello name="George" />
      <Hello name="Daisy" />
    </div>
  );
};

Array JS

const t = [1, -1, 3];

t.push(5);

console.log(t.length); // 4 est affiché
console.log(t[1]); // -1 est affiché

t.forEach((value) => {
  console.log(value); // les chiffres 1, -1, 3, 5 sont affichés, chacun sur une ligne
});

Manipulation d’array

Ajout d’élément

const t = [1, -1, 3];

// viens créer un nouveau tableau avec le nouvel élément
const t2 = t.concat(5);

console.log(t); // [1, -1, 3] est affiché
console.log(t2); // [1, -1, 3, 5] est affiché

Parcourir et modifier les éléments d’un array

map créer un nouveau tableau, la fonction passer en paramétres est utilisé pour créer un nouvel array

const t = [1, 2, 3];

const m1 = t.map((value) => value * 2);
console.log(m1); // [2, 4, 6] est affiché

// modifications des éléments du tableau
const m2 = t.map((value) => "<li>" + value + "</li>");
console.log(m2);
// [ '<li>1</li>', '<li>2</li>', '<li>3</li>' ] est affiché

Déstructuration

Permet d’affecter facilement des éléments à des variables

const t = [1, 2, 3, 4, 5];

// first et second reçoivent les éléments du tableau
// ...rest contient les éléments restant regroupé dans un nouveau tableau
const [first, second, ...rest] = t;

console.log(first, second); // 1 2 est affiché
console.log(rest); // [3, 4, 5] est affiché

Objet

Création d’objet

const object1 = {
  name: "Arto Hellas",
  age: 35,
  education: "PhD",
};

const object2 = {
  name: "Full Stack web application development",
  level: "intermediate studies",
  size: 5,
};

const object3 = {
  name: {
    first: "Dan",
    last: "Abramov",
  },
  grades: [2, 3, 5, 3],
  department: "Stanford University",
};

// accés aux valeurs
console.log(object1.name); // Arto Hellas est affiché
const fieldName = "age";
console.log(object1[fieldName]); // 35 est affiché

// modification
object1.address = "Helsinki";
// pour utiliser des variables ou des clé avec des caractères spéciaux
object1["secret number"] = 12341;

Méthode

Il est possible de déclarer des méthodes dans un objet.

const arto = {
  name: "Arto Hellas",
  age: 35,
  education: "PhD",
  // déclaration d'une méthode
  greet: function () {
    console.log("hello, my name is " + this.name);
  },
};

// ajout d'une nouvelle méthode sur l'objet
arto.growOlder = function () {
  this.age += 1;
};

arto.greet(); // "hello, my name is Arto Hellas" est affiché

Fonction

// syntaxe fléchée complète
const sum = (p1, p2) => {
  console.log(p1);
  console.log(p2);
  return p1 + p2;
};

// appelle de fonction
const result = sum(1, 5);
console.log(result);

// syntaxe simplifiée => un seul argument
const square = (p) => {
  console.log(p);
  return p * p;
};

// une expression simple
const square = (p) => p * p;

// utilisation avec map
const t = [1, 2, 3];
const tSquared = t.map((p) => p * p);
// tSquared est devenu [1, 4, 9]

// syntaxe avec function
function product(a, b) {
  return a * b;
}

const result = product(2, 6);
// result est maintenant 12

// expression de fonction
const average = function (a, b) {
  return (a + b) / 2;
};

const result = average(2, 5);
// result est maintenant 3.5

Classe

En Js, il n’existe pas vraiment de système de classe.

// déclaration d'une classe
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  greet() {
    console.log("hello, my name is " + this.name);
  }
}

const adam = new Person("Adam Ondra", 35);
adam.greet();

const janja = new Person("Janja Garnbret", 22);
janja.greet();

État des composants, gestionnaires d’event

Fonctions d’assistance aux composants

Il est possible de créer des fonction d’assistance pour réaliser des actions.

Dans l’exemple du code, la logique pour deviner l’année de naissance est séparée en une fonction qui est appelée lorsque le composant est rendu.

L’âge de la personne n’as pas besoin d’être passé en tant que paramètre de la fonction, car il peut accéder directement à toutes les props passés au composant.

const Hello = (props) => {
  // fonction d'assistance
  const bornYear = () => {
    const yearNow = new Date().getFullYear();
    return yearNow - props.age;
  };

  return (
    <div>
      <p>
        Hello {props.name}, you are {props.age} years old
      </p>

      <p>So you were probably born in {bornYear()}</p>
    </div>
  );
};

Déstructuration

Pour simplifier le code pour passer des données, on peut utiliser la déstructuration

// déstructuration de l'objet props directement dans les paramètres du composant.
// On peut directement utiliser les données dans le composant.
const Hello = ({ name, age }) => {
  const bornYear = () => new Date().getFullYear() - age;

  return (
    <div>
      <p>
        Hello {name}, you are {age} years old
      </p>
      <p>So you were probably born in {bornYear()}</p>
    </div>
  );
};

useState - changement d’état

Le hook useState permet de gérer les changements d’état d’un composant.

  • counter: contient la valeur
  • setCounter: fonction qui permet de changer la valeur de la variable
  • useState(0): initialisation du state et valeur initial

A chaque changement de valeur de la variable, le composant est re-rendu.

import { useState } from "react";

const App = () => {
  // déclaration du hook useState()
  // initialisé à 0
  const [counter, setCounter] = useState(0);

  // la fontion permet de mettre à jour la valeur d'état
  setTimeout(() => setCounter(counter + 1), 1000);

  return <div>{counter}</div>;
};

export default App;

Gestion des évènements

const App = () => {
  const [counter, setCounter] = useState(0);

  // handler d'event
  const handleClick = () => {
    console.log("clicked");
  };

  return (
    <div>
      <div>{counter}</div>
      // l'appel de la fonction se fait au clic
      <button onClick={handleClick}>plus</button>
    </div>
  );
};

On peut directement déclarer dans l’event l’instruction à déclencher

const App = () => {
  const [counter, setCounter] = useState(0);

  return (
    <div>
      <div>{counter}</div>
      // déclenche l'incrémentation de la valeur
      <button onClick={() => setCounter(counter + 1)}>plus</button>
      // déclenche la remise à zéro
      <button onClick={() => setCounter(0)}>zero</button>
    </div>
  );
};

Transimission de données aux composants => enfant vers parent

Il est recommander d’écrire des composants React petits et réutilisables dans l’application.

Lorsque plusieurs composants utilisent les mêmes données, on peut venir remonter un état partagé jusqu’a leur ancêtre commun.

Par convention, on utilise le nom de l’évènement pour passer le handler dans le composant enfant.

// Display.jsx
// Permet d'afficher la valeur
const Display = ({ counter }) => <div>{counter}</div>;

// Button.jsx
// le composant Button permet de déclencher le changement de la valeur
const Button = ({ onClick, text }) => <button onClick={onClick}>{text}</button>;

// App.jsx
const App = () => {
  const [counter, setCounter] = useState(0);

  const increaseByOne = () => setCounter(counter + 1);

  const decreaseByOne = () => setCounter(counter - 1);
  const setToZero = () => setCounter(0);

  return (
    <div>
      <Display counter={counter} />
      // on passe le handler dans le composant enfant
      <Button onClick={increaseByOne} text="plus" />
      <Button onClick={setToZero} text="zero" />
      <Button onClick={decreaseByOne} text="minus" />
    </div>
  );
};

Lorsque l’un des boutons est cliqué, le handler est exécuté. Celui ci modifie l’état du composant App avec la fonction setCounter.

Lorsque le state est modifié, le composant, et les composants enfant sont re-render


Debogage

Spread Syntaxe

En React, on ne modifie jamais directement une valeur de state, cela pouvant entrainer des effets secondaires.

Le changement d’état doit toujours être effectué en définissant l’état sur un nouvel objet.

const handleLeftClick = () => {
  const newClicks = {
    // spread => on copie l'ensembles des valeurs
    ...clicks,
    left: clicks.left + 1, // une seule valeur est modifier
  };
  setClicks(newClicks);
};

// syntaxe simplifiée
const handleLeftClick = () => setClicks({ ...clicks, left: clicks.left + 1 });

Gestion des tableaux

Chaque clic est stocker dans un tableau. Le state allClicks est initialiser sous forme de tableau.

Lorsque un clic est déclencher, celui ci est enregistré dans le tableau.

const App = () => {
  const [left, setLeft] = useState(0);
  const [right, setRight] = useState(0);

  // tableau qui enregistre les clics
  const [allClicks, setAll] = useState([]);

  // permet d'enregistrer un click
  const handleLeftClick = () => {
    // l'ajout se fait avec la méthode concat qui ne mute pas le tableau existant
    setAll(allClicks.concat("L"));
    setLeft(left + 1);
  };

  const handleRightClick = () => {
    setAll(allClicks.concat("R"));
    setRight(right + 1);
  };

  return (
    <div>
      {left}
      <button onClick={handleLeftClick}>left</button>
      <button onClick={handleRightClick}>right</button>
      {right}
      // affichage des valeurs du tableau avec join() // les valeurs sont
      afficher sous forme de string.
      <p>{allClicks.join(" ")}</p>
    </div>
  );
};

Rendu conditionnel

Le rendu du composant History dépent si le bouton à été cliquer au moins une fois.

const History = (props) => {
  // condition => si aucun clic
  if (props.allClicks.length === 0) {
    return <div>the app is used by pressing the buttons</div>;
  }
  // si des clics, on utilise ce rendu
  return <div>button press history: {props.allClicks.join(" ")}</div>;
};

const App = () => {
  // ...

  return (
    <div>
      {left}
      <button onClick={handleLeftClick}>left</button>
      <button onClick={handleRightClick}>right</button>
      {right}

      <History allClicks={allClicks} />
    </div>
  );
};

2 - Communiquer avec le serveur

Rendu de collection

map

Permet de parcourir un tableau.

const Note = ({ note }) => {
  return <li>{note.content}</li>;
};

const App = ({ notes }) => {
  return (
    <div>
      <h1>Notes</h1>
      <ul>
        {notes.map((note) => (
          <Note key={note.id} note={note} />
        ))}
      </ul>
    </div>
  );
};

Formulaire

Composant controlé

import { useState } from "react";

const App = () => {
  // stocke les numeros ajouter
  const [persons, setPersons] = useState([
    { name: "Arto Hellas", phone: "012-546-334" },
  ]);
  // permet de gerer l'input
  const [newName, setNewName] = useState("");
  const [newPhone, setNewPhone] = useState("");

  //permet d'ajouter une nouvelle personne
  const addPerson = (event) => {
    // empeche le rechargement de la page
    event.preventDefault();
    let isExisting = false;
    persons.forEach((person) => {
      if (person.name.toLowerCase() === newName.toLowerCase()) {
        // return;
        isExisting = true;
      }
    });

    if (isExisting) {
      alert(`${newName} deja present`);
      newName("");
      return;
    }

    // creation du nouvel objet a ajouter
    const personObj = {
      name: newName,
      phone: newPhone,
    };

    // concat permet d'ajouter la nouvelle personne
    setPersons(persons.concat(personObj));
    // reset de l'input
    setNewName("");
  };

  // permet de gerer le changement de valeur
  const handleAddPerson = (event) => {
    setNewName(event.target.value); // ajout dans le state de la valeur
  };

  const handleAddPhone = (event) => {
    setNewPhone(event.target.value);
  };

  return (
    <div>
      <h2>Phonebook</h2>
      // lier au handler de submit
      <form onSubmit={addPerson}>
        <h2>add a new</h2>
        <div className="">
          <label htmlFor="name">Name:</label>
          <input
            type="text"
            id="name"
            className=""
            // lier au state
            value={newName}
            // lier au handler
            onChange={handleAddPerson}
            required
          />
        </div>
        <div className="">
          <label htmlFor="phone">Phone: </label>
          <input
            type="text"
            id="phone"
            className=""
            value={newPhone}
            onChange={handleAddPhone}
            required
          />
        </div>
        <div>
          <button type="submit">add</button>
        </div>
      </form>
    </div>
  );
};

export default App;

Search Bar

import { useState } from "react";

const App = () => {
  // Gestion de la searchBar
  // input controller de la recherche
  const [newSearch, setNewSearch] = useState("");
  // permet d'activer le filtrage
  const [showAll, setShowAll] = useState(true);

  // permet de faire le filtrage => on itere sur cet objet pour l'affichage
  const personsToShow = showAll
    ? persons
    : persons.filter((person) => {
        // permet de passer tout en minuscule pour inclure les recherches
        const name = person.name.toLocaleLowerCase();
        const search = newSearch.toLocaleLowerCase();
        return name.includes(search);
      });

  const handleSearch = (event) => {
    setShowAll(false);
    setNewSearch(event.target.value);
  };

  return (
    <div>
      <h2>Phonebook</h2>
      // permet de recuperer la valeur rechercher
      <div className="searchBar">
        <label htmlFor="">Filter shown with: </label>
        <input type="text" onChange={handleSearch} />
      </div>
      <div className="repertoire">
        <h2>Numbers</h2>
        <ul>
          // parcours l'objet contenant les valeur filtrer ou l'ensemble des
          valeurs
          {personsToShow.map((person) => (
            <li key={person.id}>
              {person.name}: {person.number}
            </li>
          ))}
        </ul>
      </div>
    </div>
  );
};

export default App;

UseEffect

Hook qui permet de faire des call

import { useEffect, useState } from "react";
import SearchBar from "./SearchBar";
import Persons from "./Persons";
import PersonForm from "./PersonForm";
import axios from "axios";

const App = () => {
  // stocke les numeros ajouter
  const [persons, setPersons] = useState([]);
  // permet de gerer l'input
  const [newName, setNewName] = useState("");
  const [newPhone, setNewPhone] = useState("");

  // permet de recuperer les donnees sur le serveur => au build du composant
  useEffect(() => {
    // fais une requete vers le serveur au montage du composant
    axios.get("http://localhost:3001/persons").then((response) => {
      setPersons(response.data);
    });
  }, []);

  // ...

  return (
    <div>
      <h2>Phonebook</h2>

      <SearchBar onChange={handleSearch} />

      <PersonForm
        onSubmit={addPerson}
        newName={newName}
        handleAddPerson={handleAddPerson}
        newPhone={newPhone}
        handleAddPhone={handleAddPhone}
      />

      <Persons persons={personsToShow} />
    </div>
  );
};

export default App;