comment demarrer

Comment démarrer avec Redux pour la gestion de l’état JavaScript – CloudSavvy IT

Logo Redux

Redux est un outil de gestion d’état, conçu spécifiquement pour les applications JavaScript côté client qui dépendent fortement de données complexes et d’API externes, et fournit d’excellents outils de développement qui facilitent le travail avec vos données.

Que fait Redux ?

En termes simples, Redux est un magasin de données centralisé. Toutes vos données d’application sont stockées dans un seul objet volumineux. Les Devtools Redux facilitent la visualisation :

Un magasin de données Redux visualisé

Cet état est immuable, ce qui est un concept étrange au début, mais qui a du sens pour plusieurs raisons. Si vous souhaitez modifier l’état, vous devez envoyer un action, qui prend essentiellement quelques arguments, forme une charge utile et l’envoie à Redux. Redux passe l’état actuel à un réducteur fonction, qui modifie l’état existant et renvoie un nouvel état qui remplace l’état actuel et déclenche un rechargement des composants affectés. Par exemple, vous pouvez avoir un réducteur pour ajouter un nouvel élément à une liste, ou supprimer ou modifier un élément qui existe déjà.

Faire de cette façon signifie que vous n’obtiendrez jamais de comportement indéfini avec votre état de modification d’application à volonté. De plus, parce qu’il existe un enregistrement de chaque action et de ce qu’elle a changé, il permet le débogage dans le temps, où vous pouvez faire défiler l’état de votre application pour déboguer ce qui se passe avec chaque action (un peu comme un historique git).

Un enregistrement de chaque action

Redux peut être utilisé avec n’importe quel framework frontal, mais il est couramment utilisé avec React, et c’est ce sur quoi nous allons nous concentrer ici. Sous le capot, Redux utilise l’API contextuelle de React, qui fonctionne de la même manière que Redux et convient aux applications simples si vous souhaitez renoncer complètement à Redux. Cependant, les Devtools de Redux sont fantastiques lorsque vous travaillez avec des données complexes, et ils sont en fait plus optimisés pour éviter les rendus inutiles.

Si vous utilisez TypeScript, les choses sont beaucoup plus compliquées pour que Redux soit strictement typé. Vous voudrez plutôt suivre ce guide, qui utilise typesafe-actions pour gérer les actions et les réducteurs d’une manière conviviale.

Structurer votre projet

Tout d’abord, vous aurez envie de mettre en place votre structure de dossiers. Cela dépend de vous et des préférences de style de votre équipe, mais il existe essentiellement deux modèles principaux que la plupart des projets Redux utilisent. La première consiste simplement à diviser chaque type de fichier (action, réducteur, middleware, effet secondaire) dans son propre dossier, comme ceci :

store/
  actions/
  reducers/
  sagas/
  middleware/
  index.js

Ce n’est pas le meilleur cependant, car vous aurez souvent besoin d’un fichier d’action et d’un fichier de réduction pour chaque fonctionnalité que vous ajoutez. Il est préférable de fusionner les dossiers d’actions et de réducteurs et de les diviser par fonctionnalité. De cette façon, chaque action et le réducteur correspondant se trouvent dans le même fichier. Tu

store/
  features/
    todo/
    etc/
  sagas/
  middleware/
  root-reducer.js
  root-action.js
  index.js

Cela nettoie les importations, car vous pouvez désormais importer à la fois les actions et les réducteurs dans la même instruction en utilisant :

import { todosActions, todosReducer } from 'store/features/todos'

C’est à vous de décider si vous souhaitez conserver le code Redux dans son propre dossier (/store dans les exemples ci-dessus), ou intégrez-le dans le dossier racine src de votre application. Si vous séparez déjà le code par composant et que vous écrivez de nombreuses actions et réducteurs personnalisés pour chaque composant, vous souhaiterez peut-être fusionner le /features/ et /components/ dossiers et stockez les composants JSX avec le code réducteur.

Si vous utilisez Redux avec TypeScript, vous pouvez ajouter un fichier supplémentaire dans chaque dossier de fonctionnalités pour définir vos types.

Installation et configuration de Redux

Installez Redux et React-Redux depuis NPM :

npm install redux react-redux

Vous voudrez probablement aussi redux-devtools:

npm install --save-dev redux-devtools

La première chose que vous voudrez créer est votre magasin. Enregistrez ceci sous /store/index.js

import { createStore } from 'redux'
import rootReducer from './root-reducer'

const store = createStore(rootReducer)

export default store;

Bien sûr, votre magasin deviendra plus compliqué que cela à mesure que vous ajouterez des éléments tels que des modules complémentaires d’effets secondaires, des middleware et d’autres utilitaires tels que connected-react-router, mais c’est tout ce qui est requis pour l’instant. Ce fichier prend le réducteur racine et appelle createStore() l’utiliser, qui est exporté pour l’application à utiliser.

Ensuite, nous allons créer une simple fonctionnalité de liste de tâches. Vous voudrez probablement commencer par définir les actions requises par cette fonctionnalité et les arguments qui leur sont transmis. Créer un /features/todos/ dossier et enregistrez les éléments suivants sous types.js:

export const ADD = 'ADD_TODO'
export const DELETE = 'DELETE_TODO'
export const EDIT = 'EDIT_TODO'

Cela définit quelques constantes de chaîne pour les noms d’action. Quelles que soient les données que vous faites circuler, chaque action aura un type propriété, qui est une chaîne unique qui identifie l’action.

Vous n’êtes pas obligé d’avoir un fichier de type comme celui-ci, car vous pouvez simplement taper le nom de chaîne de l’action, mais il est préférable pour l’interopérabilité de procéder de cette façon. Par exemple, vous pourriez avoir todos.ADD et reminders.ADD dans la même application, ce qui vous évite d’avoir à taper _TODO ou _REMINDER chaque fois que vous référencez une action pour cette fonctionnalité.

Ensuite, enregistrez ce qui suit sous /store/features/todos/actions.js:

import * as types from './types.js'

export const addTodo = text => ({ type: types.ADD, text })
export const deleteTodo = id => ({ type: types.DELETE, id })
export const editTodo = (id, text) => ({ type: types.EDIT, id, text })

Cela définit quelques actions utilisant les types des constantes de chaîne, exposant les arguments et la création de charge utile pour chacun. Celles-ci n’ont pas besoin d’être entièrement statiques, car ce sont des fonctions. Un exemple que vous pouvez utiliser est la définition d’un CUID d’exécution pour certaines actions.

Le morceau de code le plus compliqué, et où vous implémenterez la plupart de votre logique métier, se trouve dans les réducteurs. Celles-ci peuvent prendre de nombreuses formes, mais la configuration la plus couramment utilisée consiste à utiliser une instruction switch qui gère chaque cas en fonction du type d’action. Enregistrez ceci sous reducer.js:

import * as types from './types.js'

const initialState = [
  {
    text: 'Hello World',
    id: 0
  }
]

export default function todos(state = initialState, action) {
  switch (action.type) {
    case types.ADD:
      return [
        ...state,
        {
          id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1,
          text: action.text
        }
      ]    

    case types.DELETE:
      return state.filter(todo =>
        todo.id !== action.id
      )

    case types.EDIT:
      return state.map(todo =>
        todo.id === action.id ? { ...todo, text: action.text } : todo
      )

    default:
      return state
  }
}

L’état est passé en argument et chaque cas renvoie une version modifiée de l’état. Dans cet exemple, ADD_TODO ajoute un nouvel élément à l’état (avec un nouvel ID à chaque fois), DELETE_TODO supprime tous les éléments avec l’ID donné, et EDIT_TODO mappe et remplace le texte de l’élément par l’ID donné.

L’état initial doit également être défini et transmis à la fonction de réduction comme valeur par défaut pour la variable d’état. Bien sûr, cela ne définit pas l’intégralité de votre structure d’état Redux, seul le state.todos section.

Ces trois fichiers sont généralement séparés dans des applications plus complexes, mais si vous le souhaitez, vous pouvez également les définir tous dans un seul fichier, assurez-vous simplement que vous importez et exportez correctement.

Une fois cette fonctionnalité terminée, connectons-la à Redux (et à notre application). Dans /store/root-reducer.js, importez le todosReducer (et tout autre réducteur de fonctionnalités du /features/ dossier), puis transmettez-le à combineReducers(), formant un réducteur racine de niveau supérieur qui est transmis au magasin. C’est là que vous configurerez l’état racine, en vous assurant de garder chaque fonctionnalité sur sa propre branche.

import { combineReducers } from 'redux';

import todosReducer from './features/todos/reducer';

const rootReducer = combineReducers({
  todos: todosReducer
})

export default rootReducer

Utiliser Redux dans React

Bien sûr, rien de tout cela n’est utile s’il n’est pas connecté à React. Pour ce faire, vous devrez envelopper l’intégralité de votre application dans un composant fournisseur. Cela garantit que l’état et les hooks nécessaires sont transmis à chaque composant de votre application.

Dans App.js ou index.js, où que vous ayez votre fonction de rendu racine, enveloppez votre application dans un <Provider>, et transmettez-le au magasin (importé de /store/index.js) comme accessoire :

import React from 'react';
import ReactDOM from 'react-dom';

// Redux Setup
import { Provider } from 'react-redux';
import store, { history } from './store';

ReactDOM.render(
    <Provider store={store}>
       <App/>
    </Provider>
    , document.getElementById('root'));

Vous êtes maintenant libre d’utiliser Redux dans vos composants. La méthode la plus simple consiste à utiliser des composants de fonction et des crochets. Par exemple, pour envoyer une action, vous utiliserez le useDispatch() hook, qui vous permet d’appeler directement des actions, par exemple dispatch(todosActions.addTodo(text)).

Le conteneur suivant a une entrée connectée à l’état React local, qui est utilisé pour ajouter une nouvelle tâche à l’état chaque fois qu’un bouton est cliqué :

import React, { useState } from 'react';

import './Home.css';

import { TodoList } from 'components'
import { todosActions } from 'store/features/todos'
import { useDispatch } from 'react-redux'

function Home() {
  const dispatch = useDispatch();
  const [text, setText] = useState("");

  function handleClick() {
    dispatch(todosActions.addTodo(text));
    setText("");
  }

  function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    setText(e.target.value);
  }

  return (
    <div className="App">
      <header className="App-header">

        <input type="text" value={text} onChange={handleChange} />

        <button onClick={handleClick}>
          Add New Todo
        </button>

        <TodoList />
      </header>
    </div>
  );
}

export default Home;

Ensuite, lorsque vous souhaitez utiliser les données stockées dans l’état, utilisez le useSelector accrocher. Cela prend une fonction qui sélectionne une partie de l’état à utiliser dans l’application. Dans ce cas, il définit le post variable à la liste actuelle des tâches. Ceci est ensuite utilisé pour rendre un nouvel élément à faire pour chaque entrée dans state.todos.

import React from 'react';
import { useSelector } from 'store'

import { Container, List, ListItem, Title } from './styles'

function TodoList() {
  const posts = useSelector(state => state.todos)

  return (
    <Container>
      <List>
        {posts.map(({ id, title }) => (
          <ListItem key={title}>
            <Title>{title} : {id}</Title>
          </ListItem>
        ))}
      </List>
    </Container>
  );
}

export default TodoList;

Vous pouvez en fait créer des fonctions de sélection personnalisées pour gérer cela pour vous, enregistrées dans le /features/ dossier un peu comme les actions et les réducteurs.

Une fois que vous avez tout configuré et compris, vous voudrez peut-être envisager la configuration de Redux Devtools, la configuration d’un middleware comme Redux Logger ou connected-react-router, ou l’installation d’un modèle à effets secondaires tel que Redux Sagas.

Laisser un commentaire