Fermer

août 9, 2021

Construire la synchronisation de l'état en temps réel du hook personnalisé React


Dans cet article, je vais vous montrer comment rechercher et extraire une logique commune en tant que fonction de crochet personnalisée. Vous apprendrez cela en créant une fonction de crochet personnalisée pour gérer la synchronisation d'état en temps réel.

Les crochets sont un nouvel ajout à React depuis la version 16.8. Ils nous offrent un moyen plus simple d'écrire une logique avec état afin qu'elle puisse être réutilisée et testée séparément. Cela fait un moment depuis sa sortie et vous l'avez peut-être utilisé dans vos applications. Si vous ne l'avez pas utilisé et que vous ne savez pas pourquoi vous en avez besoin, reportez-vous à la docs avant de poursuivre la lecture.

Les crochets vous permettent de réutiliser la logique avec état sans modifier la hiérarchie de vos composants, ce qui la rend plus facile de partager cette logique entre de nombreux composants. L'objectif de cet article est de vous montrer comment extraire une logique avec état réutilisable vers un hook personnalisé et l'utiliser dans n'importe quel composant.

L'exemple sera une table modifiable et un formulaire où les modifications de données seront propagées aux autres utilisateurs du application en temps réel. Au final, vous aurez un hook personnalisé fonctionnant comme Redux en temps réel, mais avec quelques lignes de code et l'intégration d'un service de synchronisation de données en temps réel.

Si vous voulez juste voir le code fonctionnel, rendez-vous sur sur GitHub et récupérez le code . Si vous ne souhaitez pas lire les étapes de configuration de l'application et souhaitez uniquement voir comment nous extrayons la logique en tant que fonction de crochet et refactorisons les composants, passez à la section « Création et utilisation de crochets personnalisés ”.

Configuration de l'application

Nous allons utiliser create-react-app pour amorcer une nouvelle application React et également installer react-table. Ouvrez votre application de ligne de commande et exécutez les commandes ci-dessous :

  • npx create-react-app realtime-react-hooks
  • cd realtime-react-hooks && npm i react-table@6

Avec le projet créé , nous allons ajouter les composants dont nous avons besoin. Ajoutez un nouveau fichier components/Header.js et placez-y le code ci-dessous.

import React from "react";
import logo de "../logo.svg";

const En-tête = () =>  (
  <header>
    <img src={logo} className=" App-logo" alt="logo" />
    <h1 className="App-title" >Realtime React Datagrid</h1>
  </header>
);

 export default Header;

Ajoutez un autre composant components/Form.js et collez le code dans la section ci-dessous.

import Réagissez  de "react";

const Form = () => {
  const [firstName, setFirstName] = React.useState("");
   const [lastName, setLastName] = React.useState("") ;

  const handleChange = (event) => {
    if (event.cible.nom === "firstName") setFirstName(event. cible.valeur);
    if (événement.cible.nom == = "nom")[19659078]setLastName(event.target.value);
  };

  const  handleSubmit = (événement) => {
    événement.preventDefault();
  };

  return (
    <form onSubmit= {handleSubmit}>
      <h3>Ajouter new record</h3>
      <étiquette>
        Prénom : 
        <entrée
          tapez="texte"
          nom="prénom"
          valeur={prénom}
          onChange={handleChange}
        />
      </label>
      &nbsp;&nbsp;
      <étiquette>
        Nom : 
        <entrée
          tapez="texte"
          nom="nom"
          valeur={nom}
          onChange={handleChange}
        />
      </label>
      &nbsp;&nbsp;&nbsp;
      <type d'entrée="soumettre" valeur ="Ajouter" />
    </form>
  );
}; 

export default Form;

Ceci est un simple formulaire HTML qui sera utilisé pour collecter des données qui seront ajoutées au tableau. La fonction handleSubmit ne fait rien pour le moment, mais nous la modifierons dans une section ultérieure.

Vous allez ajouter un autre composant que vous mettrez dans un fichier appelé components/Table .js. Créez ce fichier et collez-y le code ci-dessous.

import React from "react";
import ReactTable from "react -table";
import "react-table/react-table.css";

const Table = ()[19659019]=> {
  const [data, setData] = React.useState[19659013]([]);

  const renderEditableCell = (data) =>  (cellInfo) => {
    return (
      <div
        style={{ backgroundColor: "#fafafa" }}
        contenuModifiable
        SupprimerContenuModifiableAvertissement
        onBlur={(e) => {
          let ligne = donnéescellInfo.index];
          ligne[cellInfo.colonne.id] = e.target.interneHTML;
          
        }}
        dangereusementSetInnerHTML={{
          __html : data[cellInfo.index][cellInfo.colonne.id],
        }}
      />
    );
  };

  retour  (
    <ReactTable
      données={données}
      colonnes={[
        {
          En-tête : "Prénom",
          accesseur: "prénom",
          Cellule: renderEditableCell(data),
        },
        {
          En-tête : "Nom de famille",
          accesseur: "nom",
          Cellule: renderEditableCell(data),
        },
        {
          En-tête : "Nom complet",
          id: "complet",
          accesseur: (d) => (
            <div
              dangereusementSetInnerHTML={{
                __html: d.prénom + " " + d.nom,
              } }
            />
          ),
        },
      ]}
      defaultPageSize={10}
      className="-striped -highlight"
    />
  );
};

export default Table ;

Le composant Table restitue une table avec des données, et elle est modifiable. Nous utilisons la bibliothèque react-table pour y parvenir mais je n'entrerai pas dans les détails de son API. Pour cet exemple, nous définissons les en-têtes de cellule du tableau et quelles données doivent être rendues dans chaque cellule dans la fonction renderEditableCell.

Utilisation de crochets et ajout de données en temps réel

Maintenant que nous avons la base pour l'application, nous ajouterons une bibliothèque qui sera utilisée pour gérer la synchronisation des données en temps réel et utilisera des hooks pour gérer la logique avec état. Nous utiliserons Hamoni Syncqui vous permet de stocker et de récupérer des données en temps réel (et c'est moi qui l'ai fait !). Il contient un package JavaScript que nous pouvons installer à partir de npm.

Exécutez npm install hamoni-sync pour installer le package.

Pour utiliser le service, nous devons nous inscrire pour obtenir un ID d'application et ID de compte. Suivez les étapes ci-dessous pour vous inscrire et récupérer votre compte et votre identifiant d'application qui seront nécessaires plus tard.

  • Inscrivez-vous et connectez-vous au tableau de bord Hamoni.
  • Entrez le nom de votre application préférée dans le champ de texte et cliquez sur le bouton Créer. Cela devrait créer l'application et l'afficher dans la section de la liste des applications.
  • Développez la carte Account ID pour obtenir votre identifiant de compte.

Hamoni Sync Dashboard" title="Hamoni Sync Dashboard "/></p data-recalc-dims=

Vous allez utiliser la bibliothèque hamoni-sync pour récupérer des données et obtenir des mises à jour en temps réel. Vous allez créer un objet de contexte qui sera utilisé pour passer le instance de hamoni-sync aux composants qui en ont besoin. Ajoutez un nouveau fichier appelé HamoniContext.js avec le code ci-dessous :

import React from[19659012]"react";

const HamoniContext = React.createContext({})[19659013];
export default HamoniContext;

Ouvrez App.js et mettez-le à jour avec le code ci-dessous.

import React , { useState, useEffect } from "react";
import "./App.css";
 import Hamoni de "hamoni-sync";

import Header from "./components/Header";
 import Table from "./components/Table";
import Form from "./components/Form";[19659017]import HamoniContext from "./HamoniContext";

const App = () =>[19659020]{
  const accountId = "REPLACE_WITH_ACCOUNT_ID";
  const appId = "REPLACE_WITH_APP_ID"[196590513];  [hamoni, setHamoni] = useState();[19659340]useEffect(() => {
    const initialiseHamoniSync = async ()[19659019]=> {
      
      const réponse = wait fetch("https://api.sync.hamoni.tech/v1/token ", {
        méthode: "POST",
        en-têtes: {
          "Content-Type": "application/json; charset=utf-8",
        },
        body: JSON.stringify({ accountId, appId }),[19659260]});

      const jeton = attendre réponse.json() ;
      const hamoniSync = new Hamoni(token);
      attend hamoniSync.[19659055]connect();

      setHamoni(hamoniSync);
    };

    initialiseHamoniSync ();
  }, [accountId, appId]);

  return[19659020](
    <HamoniContext.Valeur du fournisseur={hamoni}>
      <d iv className="App">
        <En-tête />
        <Form />[19659117]<br />
        <Tableau />
      </div>
    < /HamoniContext.Provider>
  );
};

export default App;

Dans le code que vous venez d'ajouter, nous initialisons le client Hamoni Sync et le transmettons aux composants Form et Table à l'aide de l'API de contexte. Pour vous connecter au serveur Hamoni Sync, vous avez besoin d'un jeton d'authentification. Ceci est généré à partir d'une API spécifique comme vous pouvez le voir. Bien que nous ayons mis cela dans le code React, il est recommandé de le faire sur le backend et d'envoyer le jeton à votre client React. Ceci afin d'éviter d'exposer votre compte et votre ID d'application.

Pour le composant Formnous souhaitons stocker les données collectées et les afficher dans le tableau. Nous allons mettre à jour la logique du composant pour inclure la logique pour cela. Pour ce faire, importez le HamoniContext depuis App.js et accédez à l'instance de Hamoni Sync :

import HamoniContext from ". ./HamoniContext";

const Form = () => {
  .... code existant

  const hamoni = React.useContext(HamoniContext);
  const [syncPrimitive, setSyncPrimitive] = React.useState(null);

  Réagissez.useEffect(() => {
    if (hamoni) { 
      const getState = async () => {
        try {
          const listPrimitive = attend hamoni.get("datagrid");
          setSyncPrimitive(listPrimitive);
        } prise (erreur) {
          console.log(("Hamoni Sync Error", error));
        }[19659260]};
      getState();
    }
  }, [hamoni] );

  const handleSubmit = (event) => {
    événement.preventDefault();

    syncPrimitive.add({
      prénom: prénom,
      lastName: lastName,
    });
    setLastName("");
    setFirstName ("");
  };

  ...code existant
}

Hamoni Sync a ce qu'on appelle des primitives Sync comme moyen de stocker et de modifier l'état. Il existe trois types de primitives Sync : les primitives Valeur, Objet et Liste. Nous allons utiliser List primitive car elle nous fournit une API pour stocker et modifier les données qui doivent être stockées sous la forme d'un tableau. Vous pouvez en savoir plus sur les primitives de synchronisation dans docs.

Tout comme vous nommez des variables pour contenir vos données, vous définissez des noms pour le conteneur dans lequel vos données sont stockées. C'est le nom qui est utilisé pour récupérer les données. Dans notre exemple, nous avons utilisé hamoni.get("datagrid") pour récupérer l'objet d'état, et datagrid est le nom de l'état. Dans la fonction handleSubmitnous mettons à jour l'état en appelant syncPrimitive.add().

Nous voulons désactiver le bouton Ajouter jusqu'à ce que Hamoni Sync soit prêt . Par conséquent, nous mettrons à jour la ligne 24 pour :


Mettons à jour le composant Table pour récupérer les données de Hamoni Sync. Tout d'abord, importez le HamoniContext:

import HamoniContext from "../HamoniContext";

Ensuite, ajoutez le code ci-dessous à la fonction du composant en commençant à partir de la ligne 8.

  const hamoni = React.useContext(HamoniContext);
  const[19659020][syncPrimitive, setSyncPrimitive] = React.useState(null); 

  Réagissez.useEffect(() => {
    if (hamoni) { 
      const getState = async () => {
        try {
          const listPrimitive = attend hamoni.get("datagrid");
          setSyncPrimitive(listPrimitive);
        } prise (erreur) {
          console.log(("Hamoni Sync Error : ", error));
        } 
      };
      getState();
    }
  }, [hamoni][19659013]);

  Réagir.useEffect(() => {
    if (syncPrimitive) { 
      setData([...syncPrimitive.getAll()]) ; 

      syncPrimitive.onSync((data) => {
        setData([. ..données]);
      });

      syncPrimitive.onItemUpdated((item) => {
        setData((previous ) => [
          ...previousData.tranche(0, article .index),
          article.valeur,
          ...previousData.tranche(article.index  + 1),
        ]);
      });

      syncPrimitive.onItemAdded((item) => {
        setData((previousData ) => [...previousData, article.valeur]) ;
      });
    }
  }, [syncPrimitive]);

Nous avons ajouté deux logiques useEffect. La première consiste à obtenir un objet qui sera utilisé pour récupérer les données stockées dans Hamoni Sync, et la seconde récupère les données et met à jour l'état React, puis s'abonne pour recevoir les modifications apportées aux données. Si vous souhaitez en savoir plus sur l'API de Hamoni Sync, veuillez vous référer aux docs.

Remplacez le commentaire de la ligne 56 dans Table.js par la déclaration de code ci-dessous :[19659697]syncPrimitive.update(cellInfo.index, ligne);

Ce code est utilisé pour mettre à jour l'état dans Hamoni Sync, qui est ensuite propagé aux clients connectés.

Création et utilisation de crochets personnalisés

Maintenant, nous avons le code pour se connecter à Hamoni Sync et travailler avec les données la bibliothèque JavaScript. Nous pouvons ajouter et mettre à jour des données en temps réel. Vous avez peut-être remarqué que les composants Form et Table partagent une logique similaire pour récupérer la primitive d'état de Hamoni Sync. Nous pouvons extraire cette logique dans un hook personnalisé qui peut être utilisé dans ces composants sans dupliquer le code.

Un hook personnalisé est une fonction dont le nom commence par "use" et peut appeler d'autres fonctions de hook. Il n'a pas besoin d'avoir d'argument ou de type de retour spécifique.

Comment extraire un hook personnalisé de notre base de code actuelle ?

Si vous regardez Table et Formnous avons cette déclaration exacte dans les deux.

  React.useEffect(() => {
    if  (hamoni) {
      const getState = async () => {[19659472]try {
          const listPrimitive = wait hamoni.get("datagrid")[19659013];
          setSyncPrimitive(listPrimitive);
        } catch (error) {
          console.log(("Hamoni Sync Error : ", error));
        } 
      };
      getState();
    }
  }, [hamoni][19659013]);

Nous extrairons cette logique dans une fonction distincte que nous appellerons useSyncState et elle renverra l'objet listPrimitive.

Créez un nouvel objet. dossier nommé hooks avec un fichier nommé use-sync.js et collez le code ci-dessous dedans.

import { useState, useContext, useEffect } from "react";
import HamoniContext from "../HamoniContext"[19659013];

fonction useSyncState(nom) {
  const  hamoni = useContext(HamoniContext);
  const [syncPrimitive, setSyncPrimitive[1945902]] = useState(null);

  useEffect(() =>  {
    if (hamoni) {
      const getState = async () =>  {
        try {
          const listPrimitive = wait hamoni.get(name);
          setSyncPrimitive(listPrimitive);
        } catch (error) { 
          console.log(("Hamoni Sync Error", error));
        }[19659260]};
      getState();
    }
  }, [hamoni, name]);

  return syncPrimitive;
}

export default useSyncState;

Voilà l'avoir! Un hook personnalisé qui renvoie un objet qui sera utilisé pour accéder à l'état dans Hamoni Sync. Pour l'utiliser, nous remplacerons le code de la ligne 2 de Form.js et de la ligne 4 de Table.js par :

import useSyncState from "../hooks/use-sync"

Dans Form.js et Table.jsremplacez les lignes 8 à 23 où vous avez le useContext et useEffect hooks avec le code ci-dessous.

const syncPrimitive = useSyncState("datagrid" ;

Vous avez maintenant moins de code dans le composant et vous pouvez réutiliser la logique du hook dans n'importe quel composant. Avec la combinaison de crochets et de Hamoni Sync, nous avons une sorte de logique Redux en temps réel où il y a une seule source de vérité qui est mise à jour en temps réel.

Nous pouvons également extraire le code dans App.js pour utiliser un crochet personnalisé. Dans App.jsnous avons ce code :

 const [hamoni, setHamoni] = useState[19659013]();

useEffect(() => {
  const initialiserHamoniSync =[19659040]async () => {
    
    const réponse = wait fetch("https:/ /api.sync.hamoni.tech/v1/token", {
      méthode: "POST",
      en-têtes: {
        "Content-Type": "application/json; charset=utf-8",
      },
      body: JSON.stringify({ accountId, appId }),[19659209]});

    const jeton = attendre réponse.json() ;
    const hamoniSync = new Hamoni(token);
    wait hamoniSync.[19659055]connect();

    setHamoni(hamoniSync);
  };

  initialiseHamoniSync ();
}, [accountId, appId]);

Nous pouvons extrayez cette pièce dans un crochet personnalisé séparé. Pour ce faire, créez un nouveau fichier use-hamoni.js dans le dossier hooks. Copiez le code ci-dessous et collez-le.

 import { useState, useEffect } from "react";
 import Hamoni from "hamoni-sync";

const useHamoni = (accountId, appId) => {
  const [hamoni, setHamoni] = useState([19659013]);

  useEffect(() => {
    const initialiseHamoniSync = async  () => {
      
      const réponse = wait fetch("https://api.sync. hamoni.tech/v1/token", {
        méthode : "POST",
        en-têtes: {
          "Content-Type": "application/json; charset=utf-8",
        },
        body: JSON.stringify({ accountId, appId }),[19659260]});

      const jeton = attendre réponse.json() ;
      const hamoniSync = new Hamoni(token);
      attend hamoniSync.[19659055]connect();

      setHamoni(hamoniSync);
    };

    initialiseHamoniSync ();
  }, [accountId, appId]);

  return hamoni;
};

export default useHamoni;

We can then use this import and use th is hook in App.js. In App.jsremove the import statement for hamoni-sync and after line 8 add the import statement to the useHamoni hook.

import useHamoni from "./hooks/use-hamoni";

Then replace the useState and useEffect statements with the code statement below.

const hamoni = useHamoni(accountId, appId);

Now your App.js file should have the same content as the code below.

import React from "react";
import "./App.css";

import Header from "./components/Header";
import Table from "./components/Table";
import Form from " ./components/Form";

import HamoniContext from "./HamoniContext";
import useHamoni from "./hooks/use-hamoni";

const App = () => {
  const accountId = "REPLACE_WITH_ACCOUNT_ID";
  const appId = "REPLACE_WITH_APP_ID";
  const hamoni = useHamoni(accountId, appId);

  return (
    <HamoniContext.Provider value={hamoni}>
      <div className="App">
        <Header />
        <Form />
        <br />
        < Table />
      </div>
    </HamoniContext.Provider>
  );
};

export default App;

Now you have two custom hooks in the application and you can use this strategy in extracting logic as custom hook functions in your application.

In order to run the application and get data without errors, we need to create/initialize the state in Hamoni Sync. This is according to Hamoni Sync’s design. For this reason, you will add a script that you can run once to set up the service with data.

Add a new file seed.js and paste the code below in it:

const Hamoni = require("hamoni-sync");
const hamoni = new Hamoni("AccountID", "APP_ID");

hamoni
  .connect()
  .then(response => {
    hamoni
      .createList("datagrid", [
        { firstName: "James", lastName: "Darwin" },
        { firstName: "Jimmy", lastName: "August" }
      ])
      .then(() => console.log("create success"))
      .catch(error => console.log(error));
  })
  .catch(error => console.log(error));

Replace the AccountID and APP_ID placeholders with your account details. Then run this script from the command line by running the command node seed.js. After this is done you can run the React app with npm start and try out the application.

Custom React Hooks" title="Custom React Hooks"/></p data-recalc-dims=

You can find source code for this example on GitHub.




Source link