Introduction
Cette section explique comment utiliser les nouveaux React Hooks, leurs règles et comment vous pouvez créer vos propres Hooks. Comme vous le savez React évolue très rapidement et depuis React 16.8, les nouveaux React Hooks ont été introduits, qui changent la donne en ce qui concerne le développement de React en ce sens qu'ils vont augmenter la vitesse de codage et améliorer les performances de nos applications. React nous permet d'écrire des applications React en utilisant uniquement des composants fonctionnels, ce qui signifie qu'il n'est plus nécessaire d'utiliser des composants de classe.
Les hooks c'est quoi ?
React Hooks est un nouvel ajout dans React 16.8. Ils vous permettent d'utiliser l'état et d'autres fonctionnalités de React sans écrire de composant de classe React. Les hooks React sont également rétrocompatibles, ce qui signifie qu'ils ne contiennent aucun changement avec rupture et qu'ils ne remplacent pas votre connaissance des concepts React.
Au cours de ce chapitre, nous verrons un aperçu des Hooks pour les utilisateurs expérimentés de React, et nous allons également apprendre certains des Hooks React les plus courants tels que:
- useState
- useEffect
- useReducer
- useRef
- useImperativeHandle
- useLayoutEffect
- useMemo
- useCallback
- useDispatch && useSelector
- memo
- useDebugValue
Rules
Les React Hooks sont essentiellement des fonctions JavaScript, mais vous devez suivre deux règles pour les utiliser :
- N'appelez les hooks qu'au niveau supérieur : Documentation officielle. En d'autres termes, éviter d'appeler les hooks à l'intérieur des boucles, des conditions ou des fonctions imbriquées. Je cite : "En suivant cette règle, vous vous assurez que les crochets sont appelés dans le même ordre à chaque rendu d'un composant.''
- Appelez uniquement les hooks à partir des fonctions React : Documentation officielle. On les appelle à partir des composants de la fonction React ou à partir de hooks personnalisés.
React fournit un plugin linter pour appliquer ces règles pour vous.
npm install --save-dev eslint-plugin-react-hooks
useState
Vous savez probablement comment utiliser le state du composant en l'utilisant dans une classe avec this.setState()
. Maintenant, vous pouvez utiliser l'état du composant en utilisant le nouveau React Hook : useState
.
Importez le Hook useState
de React :
import { useState } from 'react'
Ensuite, vous devez déclarer l'état que vous souhaitez utiliser en définissant une variable d'état et le setter pour cet état spécifique :
const Counter = () => {
const [counter, setCounter] = useState<number>(0)
}
Afin de tester notre état, nous devons créer une méthode qui sera déclenchée par l'événement onClick
:
const Counter = () => {
const [counter, setCounter] = useState<number>(0)
const handleCounter = (operation: 'add' | 'subtract') => {
if (operation === 'add') {
return setCounter(counter + 1)
}
return setCounter(counter - 1)
}
}
Enfin, nous pouvons afficher l'état du counter et certains boutons pour augmenter ou diminuer le state :
return (
<p>
Counter: {counter} <br />
<button onClick={() => handleCounter('add')}>+ Add</button>
<button onClick={() => handleCounter('subtract')}>- Subtract</button>
</p>
)
Comme vous pouvez le constater, le Hook useState
change la donne dans React et facilite la gestion de l'état dans un composant fonctionnel.
Migration d'un composant de classe vers un composant fonctionnel
Considérons un exemple de migration d'un composant utilisant une classe vers les Hooks React.
class Issues extends Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = {
issues: []
}
}
}
Premièrement, nous devons détruire la classe pour la transformer en composant fonctionnel :
type Props = {
propX: string
propY: number
propZ: boolean
}
const Issues: FC<Props> = () => {...}
L'étape suivante consiste à détruire le constructeur (nous ne sommes plus dans une classe) et à y ajouter le Hook useState
:
// Le Hook useState remplace la méthode this.setState()
const [issues, setIssues] = useState<Issue[]>([])
useReducer
Le Hook useReducer
est souvent préférable à useState
lorsque vous avez une logique d'état local complexe (objet avec plusieurs niveaux) ou lorsque l'état suivant dépend de l'état précédent de cet objet. useReducer
vous permet également d'optimiser les performances pour des composants qui déclenchent des mises à jour profondes, car vous pouvez fournir dispatch
à la place de fonctions de rappel.
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Total : {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
useRef
Le Hook useRef
renvoie un objet ref modifiable dont la propriété current
est initialisée avec l'argument fourni (initialValue
). L'objet renvoyé persistera pendant toute la durée de vie du composant. Il permet d'avoir accès à un nœud dans le DOM, ce qui permet, par exemple, d'utiliser une méthode de l'enfant ou de capturer un événement.
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` fait référence au champ textuel monté dans le DOM
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Donner le focus au champ</button>
</>
);
}
useImperativeHandle
Le Hook useImperativeHandle
personnalise l'instance qui est exposée au composant parent lors de l'utilisation de ref. Comme toujours, il vaut mieux s'abstenir d'utiliser du code impératif manipulant des refs dans la plupart des cas.
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
useLayoutEffect
La signature est identique à celle de useEffect, mais useLayoutEffect s’exécute de manière synchrone après que toutes les mutations du DOM ont eu lieu. Utilisez-le pour inspecter la mise en page du DOM et effectuer un nouveau rendu de manière synchrone. Les mises à jour planifiées dans useLayoutEffect seront traitées de manière synchrone avant que le navigateur ait pu procéder à l’affichage. Préférez l’utilisation du useEffect standard chaque fois que possible, pour éviter de bloquer les mises à jour visuelles.
Si vous migrez du code depuis un composant écrit à l’aide d’une classe, sachez que useLayoutEffect s’exécute dans la même phase que componentDidMount et componentDidUpdate. Nous vous conseillons de commencer avec useEffect, et de ne tenter useLayoutEffect que si vous rencontrez des problèmes.
useEffect
Lorsque vous travaillez avec useEffect
, vous devez penser aux effets. Si vous souhaitez obtenir l'effet équivalent à la méthode componentDidMount
avec useEffect
, vous pouvez procéder comme suit :
Réalisons la migration du composant classe, pour accepter notre useEffect
(basé sur l'exemple plus haut) :
useEffect(() => {
// Here you perform your side effect
}, [])
componentDidMount() {
axios
.get('https://api.github.com/repos/ContentPI/ContentPI/issues')
.then((response: any) => {
this.setState({
issues: response.data
})
})
}
Le premier paramètre est le callback de l'effet que vous souhaitez exécuter, et le second paramètre est le tableau des dépendances. Si vous passez un tableau vide ([]
) sur les dépendances, le state et les props auront leurs valeurs initiales d'origine. Si vous transmettez un tableau de dépendances, le Hook useEffect ne s'exécutera que si l'une de ces dépendances change :
⚠️ Cependant, il est important de mentionner que même s'il s'agit de l'équivalent le plus proche de componentDidMount
, il n'a pas le même comportement. Contrairement à componentDidMount
et componentDidUpdate
, la fonction que nous passons à useEffect
est appelée après la mise en page du composant. Cela fonctionne normalement pour de nombreux effets secondaires courants, tels que la configuration des abonnements et des gestionnaires d'événements, car la plupart des types de travail ne doivent pas empêcher le navigateur de mettre à jour l'écran.
Si vous avez besoin de déclencher un effet de manière conditionnelle, vous devez ajouter une dépendance au tableau de dépendances, sinon vous exécuterez l'effet plusieurs fois et cela peut provoquer une boucle infinie.
Nous avons utilisé la méthode du cycle de vie appelée componentDidMount
, qui est exécutée lorsque le composant est monté et ne va s'exécuter qu'une seule fois. Notre Hook useEffect
gérera désormais toutes les méthodes de cycle de vie en utilisant une syntaxe différente pour chacune, voyons comment obtenir le même effet que componentDidMount
:
useEffect(() => {
// When you pass an array of dependencies the useEffect hook will only
// run if one of the dependencies changes.
}, [dependencyA, dependencyB])
// When we use the useEffect hook with an empty array [] on the
// dependencies (second parameter)
// this represents the componentDidMount method (will be executed when the
// component is mounted).
useEffect(() => {
axios
.get('https://api.github.com/repos/ContentPI/ContentPI/issues')
.then((response: any) => {
// Here we update directly our issue state
setIssues(response.data)
})
}, [])
useCallback, useMemo et memo
Afin de comprendre la différence entre useCallback
, useMemo
et memo
, nous allons faire un exemple de liste de tâches. (L'ensemble du code se trouve dans la sidebar, branche main) :
Vous pouvez supprimer l'ensemble des fichiers inutiles et changer votre App
pour qu'il ressemble à ceci :
// Dependencies
import { useState, useEffect, useMemo, useCallback } from 'react'
// Components
import List, { Todo } from './List'
const initialTodos = [
{ id: 1, task: 'Go shopping' },
{ id: 2, task: 'Pay the electricity bill'}
]
function App() {
const [todoList, setTodoList] = useState(initialTodos)
const [task, setTask] = useState('')
useEffect(() => {
console.log('Rendering <App />')
})
const handleCreate = useCallback(() => {
const newTodo = {
id: Date.now(),
task
}
// Pushing the new todo to the list
setTodoList(prevTodoList => [...prevTodoList, newTodo])
// Resetting input value
setTask('')
}, [task])
return (
<>
<input
type="text"
value={task}
onChange={(e) => setTask(e.target.value)}
/>
<button onClick={handleCreate}>Create</button>
<List todoList={todoList} />
</>
)
}
export default App
Nous définissons certaines tâches initiales et créons le state todoList
, que nous transmettrons à travers le composant List
. Créons le composant List.tsx
:
// Dependencies
import { FC, useEffect, memo } from 'react'
// Components
import Task from './Task'
// Types
export type Todo = {
id: number
task: string
}
interface Props {
todoList: Todo[]
}
const List: FC<Props> = memo(({ todoList }) => {
useEffect(() => {
// This effect is executed every new render
console.log('Rendering <List />')
})
return (
<ul>
{todoList.map((todo: Todo) => (
<Task key={todo.id} id={todo.id} task={todo.task} />
))}
</ul>
)
})
export default List
Une liste sans taches, ça ne sert à rien, donc créons ce composant :
import { FC, useEffect, memo } from 'react'
interface Props {
id: number
task: string
}
const Task: FC<Props> = memo(({ task }) => {
useEffect(() => {
console.log('Rendering <Task />', task)
})
return (
<li>{task}</li>
)
})
export default Task
On ne va pas s'attarder sur le design, ici le but est de montrer la différence entre les hooks. Voyons le résultat :
Voici le résultat du rendering de nos composants :
Comme vous pouvez le voir, en écrivant simplement "Hello", nous avons de nouveaux lots de rendus. Nous pouvons donc déterminer que ce composant n'a pas de bonnes performances, et c'est là que memo
va nous aider à améliorer les performances.
Memo
memo
est un composant d'ordre élevé (HOC), similaire à PureComponent
. Il effectue une comparaison superficielle des props (c'est-à-dire une vérification superficielle), donc si nous essayons de rendre un composant avec les mêmes accessoires tout le temps, le composant ne sera rendu qu'une seule fois et sera mémorisé. Le composant ne sera rendu que lorsqu'un props change de valeur.
Afin de corriger nos composants pour éviter les rendus multiples lorsque nous écrivons dans l'input, nous devons envelopper nos composants avec le HOC memo
:
import { FC, useEffect, memo } from 'react'
...
export default memo(List)
Mais aussi :
import { FC, useEffect, memo } from 'react'
...
export default memo(Task)
Le résultat :
Maintenant, nous n'avons plus de rendus multiples à chaque fois que nous écrivons dans l'input. Nous obtenons juste le premier lot de rendus la première fois, puis, lorsque nous écrivons, nous obtenons simplement deux autres rendus du composant App, ce qui est tout à fait correct car l'état de la tâche (valeur d'entrée) que nous modifions fait en fait partie du composant App.
Comme vous pouvez le voir, nous avons beaucoup amélioré les performances, et nous exécutons juste le besoin demandé. À ce stade, vous pensez probablement que la bonne façon est de toujours ajouter un memo
à nos composants, ou peut-être vous demandez-vous pourquoi React ne le fait pas par défaut pour nous ?
⚠️ La raison en est la performance, ce qui signifie que ce n'est pas une bonne idée d'ajouter memo
à tous nos composants à moins que ce ne soit totalement nécessaire. Sinon, le processus de comparaisons superficielles et de mémorisation aura des performances inférieures à celles que nous n'utilisons pas.
J'ai une règle pour déterminer si c'est une bonne idée d'utiliser memo
: ne l'utilisez pas. Normalement, lorsque nous avons de petits composants ou une logique de base, nous n'en avons pas besoin à moins que vous ne travailliez avec des données volumineuses provenant d'une API ou de votre composant doivent effectuer de nombreux rendus (généralement d'énormes listes), ou lorsque vous remarquez que votre application ralentit. Ce n'est que dans ce cas que je recommanderais d'utiliser memo
.
useMemo
Supposons que nous souhaitions maintenant implémenter une fonction de recherche dans notre liste de tâches. La première chose que nous devons faire est d'ajouter un nouvel état appelé term
:
function App() {
const [todoList, setTodoList] = useState(initialTodos)
const [task, setTask] = useState('')
const [term, setTerm] = useState('')
Ensuite, nous devons créer une fonction appelée handleSearch
et ajoutons le bouton:
const filteredTodoList = todoList.filter((todo: Todo) => {
console.log('Filtering...')
return todo.task.toLowerCase().includes(term.toLocaleLowerCase())
})
return (
<>
<input
type="text"
value={task}
onChange={(e) => setTask(e.target.value)}
/>
<button onClick={handleCreate}>Create</button>
<button onClick={handleSearch}>Search</button>
<List todoList={filteredTodoList} />
</>
)
Vous devriez obtenir ce résultat :
Maintenant, regardons les performances :
Le filtrage est exécuté deux fois, puis le composant App
est rendu, et tout semble bon ici, mais quel est le problème avec cela ? Essayez d'écrire "Go to the doctor" à nouveau dans l'input et voyons combien de rendus et de filtrages vous obtenez. Vous verrez que pour chaque lettre que vous écrivez, vous obtiendrez deux appels de filtrage et un rendu App et vous n'avez pas besoin d'être un génie pour voir que c'est une mauvaise performance. Sans oublier que si vous travaillez avec un grand tableau de données, ce sera pire. Alors comment pouvons-nous résoudre ce problème ?
🦸♂️ useMemo
est notre héros !!! Nous devons déplacer notre filtre à l'intérieur de useMemo
:
const filteredTodoList = useMemo(() => todoList.filter((todo: Todo) => {
console.log('Filtering...')
return todo.task.toLowerCase().includes(term.toLowerCase())
}), [])
Ce hook mémorisera le résultat (valeur) d'une fonction et aura quelques dépendances à écouter. Il reste un petit problème. Si vous essayez de cliquer sur le bouton "Rechercher", il ne filtrera pas, et c'est parce que nous avons manqué les dépendances. Vous devez ajouter les dépendances term
et todoList
au tableau :
const filteredTodoList = useMemo(() => todoList.filter((todo: Todo) => {
console.log('Filtering...')
return todo.task.toLowerCase().includes(term.toLocaleLowerCase())
}), [term, todoList])
Vous devriez obtenir les performances suivantes :
⚠️ Ici, nous devons utiliser la même règle que celle que nous avons utilisée pour memo
; ne l'utilisez qu'en cas d'absolue nécessité.
useCallback
Nous allons maintenant ajouter une fonctionnalité de suppression de tâche pour savoir comment useCallback
fonctionne. La première chose que nous devons faire est de créer une nouvelle fonction appelée handleDelete
dans notre App
:
const handleDelete = (taskId: number) => {
const newTodoList = todoList.filter((todo: Todo) => todo.id !== taskId)
setTodoList(newTodoList)
}
Ensuite, nous devons passer cette fonction au composant List
en tant que prop :
return (
<>
<input
type="text"
value={task}
onChange={(e) => setTask(e.target.value)}
/>
<button onClick={handleCreate}>Create</button>
<button onClick={handleSearch}>Search</button>
<List todoList={filteredTodoList} handleDelete={handleDelete} />
</>
);
Ensuite, dans notre List
, vous devez ajouter la nouvelle prop :
interface Props {
todoList: Todo[];
handleDelete: any;
}
Ensuite, vous devez l'extraire ce nouveau prop et le transmettre au composant Task
:
const List: FC<Props> = ({ todoList, handleDelete }) => {
useEffect(() => {
// This effect is executed every new render
console.log('Rendering <List />')
})
return (
<ul>
{todoList.map((todo: Todo) => (
<Task
key={todo.id}
id={todo.id}
task={todo.task}
handleDelete={handleDelete}
/>
))}
</ul>
)
}
Ensuite ajustons le composant Task
, pour qu'un bouton déclenche le clique de suppression :
import { FC, useEffect, memo } from "react";
interface Props {
id: number;
task: string;
handleDelete: any;
}
const Task: FC<Props> = ({ id, task, handleDelete }) => {
useEffect(() => {
console.log("Rendering <Task />", task);
});
return (
<li>
{task} <button onClick={() => handleDelete(id)}>X</button>
</li>
);
};
export default memo(Task);
Regardons les performances :
Lors du delete tout semble aller ! Par contre quand nous ajoutons du texte maintenant nous avons un nouveau soucis de performance :
A ce stade, vous vous demandez probablement, que se passe-t-il si nous avons déjà implémenté le HOC mémo pour mémoriser les composants ?
Le problème maintenant est que notre fonction handleDelete
est passée à deux composants:
- De App à List
- De List à Task
Le problème est que cette fonction est régénérée chaque fois que nous avons un nouveau rendu, dans ce cas, chaque fois que nous écrivons quelque chose. Le hook useCallback
est très similaire à useMemo
dans la syntaxe, mais la principale différence est qu'au lieu de mémoriser la valeur résultante d'une fonction, il mémorise plutôt la définition de la fonction :
const handleDelete = useCallback((taskId: number) => {
const newTodoList = todoList.filter((todo: Todo) => todo.id !== taskId)
setTodoList(newTodoList)
}, [todoList])
Nous devrions avoir résolu le soucis précédent ! Comme vous pouvez le constater, useCallback
nous aide à améliorer considérablement les performances. Dans la section suivante, vous apprendrez à mémoriser une fonction passée en argument avec useEffect
.
Il y a un cas particulier où nous aurons besoin d'utiliser useCallback
, et c'est quand nous passons une fonction comme argument à travers useEffect
, par exemple, dans notre App
. Créons un nouveau useEffect
:
const printTodoList = useCallback(() => {
console.log("Changing todoList", todoList);
}, [todoList]);
useEffect(() => {
printTodoList();
}, [todoList, printTodoList]);
Dans le cas actuel, nous avons :
- Utiliser
useCallback
, car nous manipulons un état - Ajouter
printTodoList
comme dépendance
Nous avons donc résolu le souci de performance en liant useCallback
et useEffect
.
En résumé :
memo |
useMemo |
useCallback |
---|---|---|
Mémorise un composant | Mémorise une valeur calculée | Mémorise une définition de fonction pour éviter de la redéfinir à chaque rendu. |
Se remémore lorsque les accessoires changent | Pour les propriétés calculées | Utilisez-le chaque fois qu'une fonction est passée comme argument d'effet. |
Évite les re-rendus | Pour les processus lourds | Utilisez-le chaque fois qu'une fonction est passée par des props à un composant mémorisé. |
⚠️ Et enfin, n'oubliez pas la règle d'or : ne les utilisez qu'en cas d'absolue nécessité.
useReducer
Vous avez probablement une certaine expérience de l'utilisation de Redux (react-redux) avec des composants de classe, et si tel est le cas, vous comprendrez comment useReducer
fonctionne. Les concepts sont fondamentalement les mêmes : actions, réducteurs, répartition, stockage et état. Même si, en général, cela semble très similaire à react-redux, ils ont quelques différences :
react-redux
fournit des middleware et des wrappers tels que thunk, sagas et bien d'autres encore.useReducer
vous donne simplement une méthode dispatch que vous pouvez utiliser pour envoyer des objets simples en tant qu'actions.useReducer
n'a pas de magasin par défaut ; Vous pouvez en créer un en utilisantuseContext
, mais cela ne fait que réinventer la roue.
Implémentons un exemple :
import { useReducer, useState, ChangeEvent} from 'react'
type Note = {
id: number
note: string
}
type Action = {
type: string
payload?: any
}
type ActionTypes = {
ADD: 'ADD'
UPDATE: 'UPDATE'
DELETE: 'DELETE'
}
const actionType: ActionTypes = {
ADD: 'ADD',
DELETE: 'DELETE',
UPDATE: 'UPDATE'
}
const initialNotes: Note[] = [
{
id: 1,
note: 'Note 1'
},
{
id: 2,
note: 'Note 2'
}
]
const reducer = (state: Note[], action: Action) => {
switch (action.type) {
case actionType.ADD:
return [...state, action.payload]
case actionType.DELETE:
return state.filter(note => note.id !== action.payload)
case actionType.UPDATE:
const updatedNote = action.payload
return state.map((n: Note) => n.id === updatedNote.id ? updatedNote : n)
default:
return state
}
}
const Notes = () => {
const [notes, dispatch] = useReducer(reducer, initialNotes)
const [note, setNote] = useState('')
const handleSubmit = (e: any) => {
e.preventDefault()
const newNote = {
id: Date.now(),
note
}
dispatch({ type: actionType.ADD, payload: newNote })
}
return (
<div>
<h2>Notes</h2>
<ul>
{notes.map((n: Note) => (
<li key={n.id}>
{n.note} {' '}
<button
onClick={() => dispatch({
type: actionType.DELETE,
payload: n.id
})}
>
X
</button>
<button
onClick={() => dispatch({
type: actionType.UPDATE,
payload: {...n, note}
})}
>
Update
</button>
</li>
))}
</ul>
<form onSubmit={handleSubmit}>
<input
placeholder="New note"
value={note}
onChange={e => setNote(e.target.value)}
/>
</form>
</div>
)
}
export default Notes
Nous avons défini 3 opérations : ADD
, DELETE
et UPDATE
.
dispatch
est similaire à useState
, et dans notre cas, il nous permet dans un premier temps d'initialiser notre "State": const [notes, dispatch] = useReducer(reducer, initialNotes)
.
dispatch
sert ensuite à envoyer les actions avec le type d'action que l'on veut réaliser : dispatch({ type: actionType.ADD, payload: newNote })
.
Comme vous pouvez le voir, useReducer
est à peu près le même que Redux en termes de méthode de répartition, d'actions et de réducteurs, mais la principale différence est que cela est limité au contexte de votre composant et de ses enfants. Donc, si vous avez besoin d'un magasin global pour être accessible depuis l'ensemble de votre application, vous devez utiliser react-redux
à la place.
useDebugValue
Vous pouvez utiliser useDebugValue pour afficher une étiquette pour les Hooks personnalisés dans les outils de développement React (React DevTools, NdT).
useDebugValue(value)
useDispatch && useSelector
Avant les Hooks, nous utilisions toujours connect()
, qui est un composant d'ordre supérieur et un wrapper à notre composant. connect()
permet de lire les valeurs du store Redux. connect()
prend deux arguments, tous deux facultatifs :
mapStateToProps
: appelé à chaque fois que l'état du store change. Il reçoit l'intégralité de l'état du store et doit renvoyer un objet de données dont ce composant a besoin.mapDispatchToProps
: Ce paramètre peut être soit une fonction, soit un objet. S'il s'agit d'une fonction, elle sera appelée une fois lors de la création du composant. Il recevradispatch
comme argument et devrait renvoyer un objet plein de fonctions qui utilisentdispatch
pour répartir les actions.
useDispatch
C'est l'équivalent de mapDispatchToProps
. Nous allons appeler useDispatch
et le stocker dans une variable dispatch
. Ce Hook renvoie une référence à la fonction dispatch
du store.
import React from "react";
//import useDispatch from react-redux
import { useDispatch} from "react-redux";
//these are actions define in redux>actions folder
import { updateFirstName } from "../redux/actions";
const Form = () => {
const dispatch = useDispatch();
const handleFirstName = () => {
//dispatching the action
dispatch(updateFirstName("Jason"));
};
return (
<React.Fragment>
<div className="container">
<button onClick={handleFirstName}>Update First
Name</button>
</div>
</React.Fragment>
);
};
export default Form;
useSelector
C'est l'équivalent de mapStateToProps
. useSelector
est une fonction qui prend l'état actuel comme argument et renvoie toutes les données que vous voulez. Elle vous permet de stocker les valeurs de retour dans une variable dans le cadre de vos composants fonctionnels, au lieu de les transmettre comme props.
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { updateFirstName } from "../redux/actions";
const Form = () => {
const dispatch = useDispatch();
const nameObj = useSelector((state) => state.nameReducer);
const { firstName } = nameObj;
const handleFirstName = () => {
dispatch(updateFirstName("Jason"));
};
return (
<React.Fragment>
<div className="container">
<label>First Name : {firstName}</label>
<button onClick={handleFirstName}>Update First Name</button>
<label>Last Name : {lastName}</label>
<button type="submit" onClick={handleLastName}>
Update First Name
</button>
</div>
</React.Fragment>
);
};
export default Form;
Migration des cycles de vies
Comment vous le savez, nous avons plusieurs cycles de vie en React :
Nous allons lister comment vous pouvez migrer les anciens cycles de vie pour qu'ils fonctionnent avec les nouveaux hooks :
Lifecycle | Hooks | Spécification |
---|---|---|
componentDidMount |
useEffect |
useEffect(() => {}, []) : vous devez passer un tableau vide comme argument |
componentDidUpdate |
useEffect |
useEffect(() => {}) : vous ne devez pas passer d'argument |
componentWillUnmount |
useEffect |
useEffect(() => { return () => {} }, []) : nous utilisons la fonction return qui retourne une fonction qui sera appelée à la destruction |
Conclusion
J'espère que vous avez apprécié la lecture qui regorge de très bonnes informations concernant les nouveaux React Hooks. Jusqu'à présent, vous avez appris :
- Comment fonctionnent les nouveaux Hooks
- Comment récupérer des données avec des Hooks
- Comment migrer un composant de classe vers React Hooks
- Comment fonctionnent les effets
- La différence entre
memo
,useMemo
etuseCallback
- Comment
useReducer
fonctionne et la principale différence par rapport à React-Redux
Toutes ces connaissances vous aideront à améliorer les performances de vos composants React.