Gestion des ressources avec le hook 'use' de React : Optimiser les cycles de vie des ressources pour une performance maximale | MLOG | MLOG

Explication :

Exemple 2 : Gérer les connexions WebSocket

Cet exemple montre comment gérer une connexion WebSocket en utilisant le hook "use" et un enveloppeur de ressource personnalisé.

            import React, { useState, useEffect, use } from 'react';

const createWebSocketResource = (url) => {
  let socket;
  let status = 'pending';
  let messageQueue = [];
  let listeners = [];

  const connect = () => {
    return new Promise((resolve, reject) => {
      socket = new WebSocket(url);

      socket.onopen = () => {
        status = 'connected';
        resolve();
        // Envoyer les messages en file d'attente
        messageQueue.forEach(msg => socket.send(msg));
        messageQueue = [];
      };

      socket.onerror = (error) => {
        status = 'error';
        reject(error);
      };

      socket.onmessage = (event) => {
        listeners.forEach(listener => listener(event.data));
      };

      socket.onclose = () => {
        status = 'closed';
        listeners = []; // Vider les écouteurs pour éviter les fuites de mémoire
      };
    });
  };

  const promise = connect();

  return {
    read() {
      use(promise);
    },
    send(message) {
      if (status === 'connected') {
        socket.send(message);
      } else {
        messageQueue.push(message);
      }
    },
    subscribe(listener) {
      listeners.push(listener);
      return () => {
        listeners = listeners.filter(l => l !== listener);
      };
    },
    close() {
        if (socket && socket.readyState !== WebSocket.CLOSED) {
            socket.close();
        }
    }
  };
};

function WebSocketComponent({ url }) {
  const socketResource = createWebSocketResource(url);
  // Suspendre jusqu'Ă  la connexion
  socketResource.read();
  const [message, setMessage] = useState('');
  const [receivedMessages, setReceivedMessages] = useState([]);

  useEffect(() => {
    const unsubscribe = socketResource.subscribe(data => {
      setReceivedMessages(prevMessages => [...prevMessages, data]);
    });
    return () => {
        unsubscribe();
        socketResource.close();
    };
  }, [socketResource]);

  const sendMessage = () => {
    socketResource.send(message);
    setMessage('');
  };

  return (
    
setMessage(e.target.value)} />
Messages reçus :
    {receivedMessages.map((msg, index) => (
  • {msg}
  • ))}
); } function App() { return ( Connexion au WebSocket...
}> ); } export default App;

Explication :

Exemple 3 : Gérer les descripteurs de fichiers

Cet exemple illustre la gestion des ressources avec le hook "use" en utilisant les descripteurs de fichiers de NodeJS (cela ne fonctionnera que dans un environnement NodeJS et vise à présenter les concepts du cycle de vie des ressources).

            // Cet exemple est conçu pour un environnement NodeJS

const fs = require('node:fs/promises');
import React, { use } from 'react';

const createFileHandleResource = async (filePath) => {
  let fileHandle;

  const openFile = async () => {
    fileHandle = await fs.open(filePath, 'r');
    return fileHandle;
  };

  const promise = openFile();

  return {
    read() {
      return use(promise);
    },
    async close() {
      if (fileHandle) {
        await fileHandle.close();
        fileHandle = null;
      }
    },
    async readContents() {
      const handle = use(promise);
      const buffer = await handle.readFile();
      return buffer.toString();
    }
  };
};


function FileViewer({ filePath }) {
  const fileHandleResource = createFileHandleResource(filePath);
  const contents = fileHandleResource.readContents();

  React.useEffect(() => {
    return () => {
      // Nettoyage lorsque le composant est démonté
      fileHandleResource.close();
    };
  }, [fileHandleResource]);

  return (
    

Contenu du fichier :

{contents}
); } // Exemple d'utilisation async function App() { const filePath = 'example.txt'; await fs.writeFile(filePath, 'Bonjour, le monde !\nCeci est un fichier de test.'); return (
); } export default App;

Explication :

Techniques avancées : Error Boundaries, mutualisation des ressources et composants serveur

Au-delà des exemples de base, le hook "use" peut être combiné avec d'autres fonctionnalités de React pour mettre en œuvre des stratégies de gestion de ressources plus sophistiquées.

Error Boundaries : Gérer les erreurs avec élégance

Les Error Boundaries sont des composants React qui attrapent les erreurs JavaScript n'importe où dans leur arbre de composants enfants, enregistrent ces erreurs et affichent une interface utilisateur de repli au lieu de faire planter tout l'arbre de composants. Lorsque vous utilisez le hook "use", il est crucial d'envelopper vos composants avec des Error Boundaries pour gérer les erreurs potentielles lors de la récupération de données ou de l'initialisation des ressources.

            import React, { Component } from 'react';

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // Mettre à jour l'état pour que le prochain rendu affiche l'UI de repli.
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // Vous pouvez également journaliser l'erreur dans un service de rapport d'erreurs
    console.error(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // Vous pouvez rendre n'importe quelle UI de repli personnalisée
      return 

Quelque chose s'est mal passé.

; } return this.props.children; } } function App() { return ( Chargement...
}> ); }

Mutualisation des ressources : Optimiser la réutilisation des ressources

Dans certains scénarios, la création et la destruction fréquentes de ressources peuvent être coûteuses. La mutualisation des ressources (resource pooling) consiste à maintenir un pool de ressources réutilisables pour minimiser la surcharge liée à la création et à la destruction de ressources. Bien que le hook "use" n'implémente pas intrinsèquement la mutualisation des ressources, il peut être utilisé conjointement avec une implémentation de pool de ressources distincte.

Considérez un pool de connexions à une base de données. Au lieu de créer une nouvelle connexion pour chaque requête, vous pouvez maintenir un pool de connexions préétablies et les réutiliser. Le hook "use" peut être utilisé pour gérer l'acquisition et la libération des connexions du pool.

(Exemple conceptuel - L'implémentation varie en fonction de la ressource spécifique et de la bibliothèque de pooling) :

            // Exemple conceptuel (pas une implémentation complète et exécutable)

import React, { use } from 'react';
// Supposons qu'une bibliothèque de pool de connexions de base de données existe
import { getConnectionFromPool, releaseConnectionToPool } from './dbPool';

const createDbConnectionResource = () => {
  let connection;

  const acquireConnection = async () => {
    connection = await getConnectionFromPool();
    return connection;
  };

  const promise = acquireConnection();

  return {
    read() {
      return use(promise);
    },
    release() {
      if (connection) {
        releaseConnectionToPool(connection);
        connection = null;
      }
    },
    query(sql) {
      const conn = use(promise);
      return conn.query(sql);
    }
  };
};

function MyDataComponent() {
  const dbResource = createDbConnectionResource();

  React.useEffect(() => {
    return () => {
      dbResource.release();
    };
  }, [dbResource]);

  const data = dbResource.query('SELECT * FROM my_table');
  return 
{data}
; }

Composants Serveur React (RSC) : Le foyer naturel du hook "use"

Le hook "use" a été initialement conçu pour les React Server Components. Les RSC s'exécutent sur le serveur, vous permettant de récupérer des données et d'effectuer d'autres opérations côté serveur sans envoyer de code au client. Cela améliore considérablement les performances et réduit la taille des paquets JavaScript côté client.

Dans les RSC, le hook "use" peut être utilisé pour récupérer directement des données de bases de données ou d'API sans avoir besoin de bibliothèques de récupération de données côté client. Les données sont récupérées sur le serveur, et le HTML résultant est envoyé au client, où il est hydraté par React.

Lorsque vous utilisez le hook "use" dans les RSC, il est important d'être conscient de leurs limitations, telles que l'absence d'état côté client et de gestionnaires d'événements. Cependant, les RSC peuvent être combinés avec des composants côté client pour créer des applications puissantes et efficaces.

Bonnes pratiques pour une gestion efficace des ressources avec "use"

Pour maximiser les avantages du hook "use" pour la gestion des ressources, suivez ces bonnes pratiques :

Pièges courants et comment les éviter

Bien que le hook "use" offre de nombreux avantages, il est important d'être conscient des pièges potentiels et de savoir comment les éviter.

Conclusion : Adopter le hook "use" pour des applications React optimisées

Le hook "use" de React représente une avancée significative dans la gestion des ressources au sein des applications React. En simplifiant la gestion des données asynchrones, en automatisant le nettoyage des ressources et en s'intégrant de manière transparente avec Suspense, il permet aux développeurs de créer des applications plus performantes, maintenables et conviviales.

En comprenant les concepts fondamentaux, en explorant des exemples pratiques et en suivant les bonnes pratiques, vous pouvez tirer parti efficacement du hook "use" pour optimiser les cycles de vie des ressources et libérer tout le potentiel de vos applications React. À mesure que React continue d'évoluer, le hook "use" jouera sans aucun doute un rôle de plus en plus important dans l'avenir de la gestion des ressources dans l'écosystème React.