Управління ресурсами з хуком 'use' в React: оптимізація життєвих циклів ресурсів для максимальної продуктивності | MLOG | MLOG

Пояснення:

Приклад 2: Управління з'єднаннями WebSocket

Цей приклад демонструє, як керувати з'єднанням WebSocket за допомогою хука "use" та спеціальної обгортки ресурсу.

            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();
        // Send queued messages
        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 = []; // Clear listeners to avoid memory leaks
      };
    });
  };

  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);
  // Suspend until connected
  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)} />
Received Messages:
    {receivedMessages.map((msg, index) => (
  • {msg}
  • ))}
); } function App() { return ( Connecting to WebSocket...
}> ); } export default App;

Пояснення:

Приклад 3: Управління дескрипторами файлів

Цей приклад ілюструє управління ресурсами за допомогою хука "use", використовуючи дескриптори файлів NodeJS (це буде працювати лише в середовищі NodeJS і призначено для демонстрації концепцій життєвого циклу ресурсів).

            // This example is designed for a NodeJS environment

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 () => {
      // Cleanup when the component unmounts
      fileHandleResource.close();
    };
  }, [fileHandleResource]);

  return (
    

File Contents:

{contents}
); } // Example Usage async function App() { const filePath = 'example.txt'; await fs.writeFile(filePath, 'Hello, world!\nThis is a test file.'); return (
); } export default App;

Пояснення:

Просунуті техніки: межі помилок, пули ресурсів та серверні компоненти

Крім базових прикладів, хук "use" можна поєднувати з іншими функціями React для реалізації більш складних стратегій управління ресурсами.

Межі помилок: витончена обробка помилок

Межі помилок (Error boundaries) — це компоненти React, які перехоплюють помилки JavaScript у будь-якому місці свого дочірнього дерева компонентів, реєструють ці помилки та відображають резервний UI замість того, щоб аварійно завершувати все дерево компонентів. При використанні хука "use" вкрай важливо обгортати ваші компоненти межами помилок для обробки потенційних помилок під час завантаження даних або ініціалізації ресурсів.

            import React, { Component } from 'react';

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

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // You can also log the error to an error reporting service
    console.error(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return 

Something went wrong.

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

Пули ресурсів: оптимізація повторного використання ресурсів

У деяких сценаріях часте створення та знищення ресурсів може бути дорогим. Пули ресурсів (Resource pooling) передбачають підтримку пулу ресурсів для повторного використання, щоб мінімізувати накладні витрати на створення та знищення ресурсів. Хоча хук "use" сам по собі не реалізує пули ресурсів, його можна використовувати разом з окремою реалізацією пулу ресурсів.

Розглянемо пул з'єднань з базою даних. Замість створення нового з'єднання для кожного запиту, ви можете підтримувати пул попередньо встановлених з'єднань і повторно їх використовувати. Хук "use" можна використовувати для управління отриманням та звільненням з'єднань з пулу.

(Концептуальний приклад - реалізація залежить від конкретного ресурсу та бібліотеки для пулінгу):

            // Conceptual Example (not a complete, runnable implementation)

import React, { use } from 'react';
// Assume a database connection pool library exists
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}
; }

Серверні компоненти React (RSC): природне середовище для хука "use"

Хук "use" спочатку був розроблений для серверних компонентів React. RSC виконуються на сервері, дозволяючи вам завантажувати дані та виконувати інші серверні операції без відправки коду клієнту. Це значно покращує продуктивність та зменшує розміри клієнтських JavaScript-бандлів.

У RSC хук "use" можна використовувати для прямого завантаження даних з баз даних або API без потреби в клієнтських бібліотеках для завантаження даних. Дані завантажуються на сервері, і отриманий HTML відправляється клієнту, де він гідратується React.

При використанні хука "use" в RSC важливо знати про обмеження RSC, такі як відсутність клієнтського стану та обробників подій. Однак RSC можна поєднувати з клієнтськими компонентами для створення потужних та ефективних застосунків.

Найкращі практики для ефективного управління ресурсами за допомогою "use"

Щоб максимізувати переваги хука "use" для управління ресурсами, дотримуйтесь цих найкращих практик:

Поширені помилки та як їх уникнути

Хоча хук "use" пропонує численні переваги, важливо знати про потенційні помилки та як їх уникнути.

Висновок: використання хука "use" для оптимізованих React-застосунків

Хук "use" в React є значним кроком вперед в управлінні ресурсами в React-застосунках. Спрощуючи обробку асинхронних даних, автоматизуючи очищення ресурсів та безшовно інтегруючись із Suspense, він дає розробникам можливість створювати більш продуктивні, підтримувані та зручні для користувача застосунки.

Розуміючи основні концепції, досліджуючи практичні приклади та дотримуючись найкращих практик, ви можете ефективно використовувати хук "use" для оптимізації життєвих циклів ресурсів та розкриття повного потенціалу ваших React-застосунків. У міру того, як React продовжує розвиватися, хук "use", безсумнівно, відіграватиме все більш важливу роль у формуванні майбутнього управління ресурсами в екосистемі React.