251 lines
No EOL
7.2 KiB
Markdown
251 lines
No EOL
7.2 KiB
Markdown
# Les bases de React
|
|
|
|
Pour commencer, je vous recommande fortement de regarder cette vidéo qui expliquera mieux que ce document sur comment fonctionne React : https://www.youtube.com/watch?v=Tn6-PIqc4UM
|
|
|
|
Ce document fera quand même de son mieux pour expliquer les bases de React et permettra d'expliquer comment intégrer un composant dans le projet.
|
|
|
|
## Qu'est-ce qu'un composant React ?
|
|
|
|
Un composant React peut être décrite comme une classe (`class component`) ou une fonction (`functional component`) retournant une description de composant en JSX, un langage permettant de combiner du HTML et du JavaScript. Ces composants ont par convention l'extension de fichier `.jsx` (ou `.tsx` avec TypeScript)
|
|
|
|
Un composant est principalement constituer de `props` (propriétés) et d'un `state` (état). Cela est universel pour tous les composants.
|
|
|
|
Voici un basique exemple de composant :
|
|
|
|
```jsx
|
|
function Welcome(props) {
|
|
return <h1>Hello, {props.name}</h1>;
|
|
}
|
|
```
|
|
|
|
> La règle d'or est que le rendu change lorsqu'un props ou un state change.
|
|
|
|
Selon comment vous écrivez le composant, classe ou fonctionnelle, la manière de changer les props ou state est différente:
|
|
|
|
Pour les props :
|
|
|
|
```tsx
|
|
// functional
|
|
function Welcome(props) {
|
|
return <h1>Hello, {props.name}</h1>;
|
|
}
|
|
|
|
// class
|
|
class Welcome extends React.Component {
|
|
render() {
|
|
return <h1>Hello, {this.props.name}</h1>;
|
|
}
|
|
}
|
|
```
|
|
|
|
Pour l'état:
|
|
|
|
```jsx
|
|
// Functionnal
|
|
const useTicking = (setDate) => {
|
|
React.useEffect(() => {
|
|
const timerID = setInterval(() => {
|
|
setDate(new Date());
|
|
}, 1000);
|
|
|
|
return () => {
|
|
clearInterval(timerID)
|
|
}
|
|
}, []);
|
|
};
|
|
|
|
export const Clock = (props) => {
|
|
const [date, setDate] = React.useState(new Date());
|
|
|
|
useTicking(setDate);
|
|
|
|
return (
|
|
<div>
|
|
{ date.toLocaleTimeString() }
|
|
</div>
|
|
);
|
|
};
|
|
|
|
|
|
// class
|
|
class Clock extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {date: new Date()};
|
|
}
|
|
|
|
componentDidMount() {
|
|
this.timerID = setInterval(
|
|
() => this.tick(),
|
|
1000
|
|
);
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
clearInterval(this.timerID);
|
|
}
|
|
|
|
tick() {
|
|
this.setState({
|
|
date: new Date()
|
|
});
|
|
}
|
|
|
|
render() {
|
|
return (
|
|
<div>
|
|
{this.state.date.toLocaleTimeString()}
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
Dans ce projet, il est de convention d'écrire en fonctionnel, car cela a pour avantage de fractionner les composants possèdant beaucoup de fonctions (voir par exemple `ContainerOperations.ts`) et de séparer les les propriétés d'états par des `state hooks` (crochet d'états).
|
|
|
|
La documentation qui suivra utilisera donc l'écriture fonctionnelle. Pour en savoir plus sur l'écriture de classe, vous pouvez essayer [le tutoriel de React](https://reactjs.org/tutorial/tutorial.html).
|
|
|
|
|
|
## Props
|
|
|
|
Les props sont des données entrer d'un composant. Comme pour un composant html ils décrivent simplement ce que le composant doit représenter.
|
|
|
|
Exemple :
|
|
|
|
```tsx
|
|
function Welcome(props) {
|
|
return <h1>Hello, {props.name}</h1>;
|
|
}
|
|
```
|
|
|
|
## State vs Stateless
|
|
|
|
Un composant possédant un état interne utilise `React.setState(newState)` dans un composant de classe ou `React.useState<Type>(defaultValue)` dans un composant fonctionnel.
|
|
Un composant ne possédant pas d'état est dit `stateless`.
|
|
|
|
Voici donc un exemple de composant React **avec état** :
|
|
|
|
```tsx
|
|
// Clock.tsx
|
|
interface IClockProps {
|
|
}
|
|
|
|
const useTicking = (setTime: React.Dispatch<React.SetStateAction<Date>>) => {
|
|
React.useEffect(() => {
|
|
const timerID = setInterval(() => {
|
|
setTime(new Date());
|
|
}, 1000);
|
|
return () => {
|
|
clearInterval(timerID)
|
|
}
|
|
}, []);
|
|
};
|
|
|
|
export const Clock: React.FC<IClockProps> = (props) => {
|
|
const [time, setTime] = React.useState<Date>(new Date());
|
|
|
|
useTicking(setTime);
|
|
|
|
return (
|
|
<div>
|
|
{ time.toLocaleTimeString() }
|
|
</div>
|
|
);
|
|
};
|
|
|
|
```
|
|
|
|
Et voici la version **sans état** :
|
|
|
|
```tsx
|
|
// Clock.tsx
|
|
interface IClockProps {
|
|
time: Date
|
|
}
|
|
|
|
export const Clock: React.FC<IClockProps> = ({ time }) => {
|
|
return (
|
|
<div>
|
|
{ time.toLocaleTimeString() }
|
|
</div>
|
|
);
|
|
};
|
|
|
|
// Parent.tsx
|
|
const useTicking = (setTime: React.Dispatch<React.SetStateAction<Date>>) => {
|
|
React.useEffect(() => {
|
|
const timerID = setInterval(() => {
|
|
setTime(new Date());
|
|
}, 1000);
|
|
return () => {
|
|
clearInterval(timerID);
|
|
};
|
|
}, []);
|
|
};
|
|
|
|
export const Parent: React.FC<IClockProps> = ({ time }) => {
|
|
const [time, setTime] = React.useState<Date>(new Date());
|
|
|
|
useTicking(setTime);
|
|
|
|
return (
|
|
<Clock time={time}>
|
|
);
|
|
};
|
|
```
|
|
|
|
Comme vous pouvez le voir, la différence est que la gestion d'état est donnée au composant parent. Cela permet de réduire les mises à jours de rendu en parallèles et de faire le rendu seulement si l'état du composant parent change.
|
|
|
|
Vous avez probablement remarqué l'usage de `React.useState()` et `React.useEffect()`. Lorsque l'on utilise une fonction avec le mot clé `use` de React, on utilise ce qu'on appelle un `hook` (crochet).
|
|
|
|
Il existe plusieurs type de hook et chacun ont leurs propre effet. Dans ce projet, nous utilisont principalement `useState` et `useEffect` mais il y a aucune interdiction pour utiliser d'autres types.
|
|
|
|
### `useState`
|
|
|
|
`useState` permet de lier à l'état interne du composant une variable.
|
|
|
|
La fonction nous fourni 2 paramètres dans une liste. La valeur courante et le setter de cette valeur :
|
|
|
|
```jsx
|
|
const [value, setValue] = React.useState(defaultValue);
|
|
```
|
|
|
|
### `useEffect`
|
|
|
|
`useEffect` permet de lancer une fonction d'effet lors d'un rendu. Il prend en paramètres :
|
|
- une fonction callback d'effet lors du rendu qui retourne lui aussi retourne une fonction d'effet lors de la suppression du rendu.
|
|
- Il prend aussi optionnellement un liste de dépendance permettant de déterminer les conditions de rendu
|
|
|
|
Revenons sur l'exemple de `useTicking`:
|
|
|
|
```tsx
|
|
const useTicking = (setTime) => {
|
|
React.useEffect(() => {
|
|
// componentDidMount
|
|
const timerID = setInterval(() => {
|
|
setTime(new Date());
|
|
}, 1000);
|
|
|
|
return () => {
|
|
// componentWillUnmount
|
|
clearInterval(timerID);
|
|
};
|
|
}, []);
|
|
};
|
|
```
|
|
|
|
Décrivons ce quelle fait :
|
|
- La callback de `useEffect` appelle `setInterval` afin d'exécuter une fonction `setTime` toute les seconde.
|
|
- Cette callback retourne la fonction de suppression de `setInterval` qui sera exécutée si le composant est disposé.
|
|
- `useEffect` possède un tableu de dépendance mais elle est vide. Cela veut dire que la callback sera exécuté qu'une seule est unique fois sur toute la durée de vie du composant
|
|
|
|
Si on avait enlevé le tableau de dépendance, on aurait eu une boucle infinie ! Car rien n'aurait limité l'exécution de la callback à chaque rendu créé par `setTime`.
|
|
|
|
> Une règle d'or est qu'il faut mettre un tableau de dépendance lorsque l'on utilise un setteur d'état car il y aura a coup sûr une boucle infinie s'il y en a pas
|
|
|
|
> Contraposée: Il n'est pas nécessaire de mettre un tableau de dépendances s'il n'y pas de setteur d'état dans la callback
|
|
|
|
> Il est de convention de mettre les fonctions `useEffect` dans une fonction nommée afin de déterminer ce qu'elle fait. Il est possible d'appeler plusieurs fois `useEffect`, donc il n'est pas dangereux de dissocier les effets.
|
|
|
|
|
|
Si vous avez compris, essayons de créer notre [premier composant React](PremierComposantReact.md). |