DEV Community

Việt Anh Trần Hữu
Việt Anh Trần Hữu

Posted on

Top 5 Custom Hook For React

Chào mọi người lại là Slao đây,
Sau đây sẽ là top 5 Custom Hook thuận tiện và mới lạ để mọi người có thể sử dụng

useMediaQuery

useMediaQuery lắng nghe các truy vấn media, cho phép bạn áp dụng các kiểu hoặc hành vi cụ thể dựa trên kích thước thiết bị

import { useEffect, useState } from 'react';

function useMediaQuery(query) {
  const [matches, setMatches] = useState(false);

  useEffect(() => {
    const mediaQuery = window.matchMedia(query);
    setMatches(mediaQuery.matches);

    const handler = event => setMatches(event.matches);
    mediaQuery.addEventListener('change', handler);

    return () => mediaQuery.removeEventListener('change', handler);
  }, [query]);

  return matches;
}
Enter fullscreen mode Exit fullscreen mode

Cách dùng

const isLargeScreen = useMediaQuery('(min-width: 1024px)');

Enter fullscreen mode Exit fullscreen mode

useWebSocket

useWebSocket hook kết nối ứng dụng của bạn với WebSocket server, dễ dàng gửi và nhận real-time dữ liệu. Nó xử lí các kết nối lại, message buffering, và event-base handling, làm cho dễ dàng cập nhập thời gian thực

Ví dụ này trình bày một hook WebSocket có thể tái sử dụng với các tính năng quản lý kết nối, xử lý sự kiện và dọn dẹp gọn gàng.

import { useState, useEffect, useRef, useCallback } from 'react';

function useWebSocket(url, options = {}) {
  const { reconnect = true, reconnectInterval = 5000, onOpen, onMessage, onError, onClose } = options;
  const [isConnected, setIsConnected] = useState(false);
  const [lastMessage, setLastMessage] = useState(null);
  const websocketRef = useRef(null);
  const reconnectTimeout = useRef(null);

  const connect = useCallback(() => {
    websocketRef.current = new WebSocket(url);

    websocketRef.current.onopen = (event) => {
      setIsConnected(true);
      onOpen && onOpen(event);
    };

    websocketRef.current.onmessage = (event) => {
      setLastMessage(event.data);
      onMessage && onMessage(event);
    };

    websocketRef.current.onerror = (event) => {
      onError && onError(event);
    };

    websocketRef.current.onclose = (event) => {
      setIsConnected(false);
      onClose && onClose(event);
      if (reconnect) {
        reconnectTimeout.current = setTimeout(connect, reconnectInterval);
      }
    };
  }, [url, reconnect, reconnectInterval, onOpen, onMessage, onError, onClose]);

  const sendMessage = useCallback((message) => {
    if (isConnected && websocketRef.current) {
      websocketRef.current.send(message);
    }
  }, [isConnected]);

  useEffect(() => {
    connect();

    return () => {
      if (websocketRef.current) {
        websocketRef.current.close();
      }
      clearTimeout(reconnectTimeout.current);
    };
  }, [connect]);

  return { isConnected, sendMessage, lastMessage };
}
Enter fullscreen mode Exit fullscreen mode

Các tính năng của useWebSocket

  • Reconnection Handling: nếu kết nối đóng, nó sẽ tự động kết nối lại sau 1 khoảng thời gian nhất định
  • Event Handling: chấp nhận các thuộc tính gọi lại onOpen, onMessage, onError, and onClose events để xử lí khi cần thiết
  • Message Sending: cung cấp chúc năng sendMessage cái mà chỉ gửi message khi websocket mở
  • Last Message Storage: lưu dữ liệu mới nhất nhận được , chấp nhận các dữ liệu gần nhất mà không cần đăng kí.

Cách dùng

function ChatApp() {
  const { isConnected, sendMessage, lastMessage } = useWebSocket('ws://localhost:4000/chat', {
    reconnect: true,
    reconnectInterval: 3000,
    onOpen: () => console.log('Connected to WebSocket'),
    onMessage: (event) => console.log('New message received:', event.data),
    onClose: () => console.log('Disconnected from WebSocket'),
  });

  const [inputValue, setInputValue] = useState('');

  const handleSend = () => {
    sendMessage(inputValue);
    setInputValue('');
  };

  return (
    <div>
      <h3>WebSocket Chat</h3>
      <div>
        <p>{isConnected ? 'Connected' : 'Disconnected'}</p>
        <input
          type="text"
          value={inputValue}
          onChange={(e) => setInputValue(e.target.value)}
          placeholder="Type your message..."
        />
        <button onClick={handleSend} disabled={!isConnected}>
          Send
        </button>
      </div>
      <div>
        <h4>Last Message:</h4>
        <p>{lastMessage}</p>
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

useHover

Phát hiện di chuột qua 1 phần tử để xử lí cho các công cụ, hình ảnh động hoặc bất kì phản hồi UI nào trên trạng thái di chuột

import { useState, useRef, useEffect } from 'react';

function useHover() {
  const [hovered, setHovered] = useState(false);
  const ref = useRef(null);

  useEffect(() => {
    const handleMouseOver = () => setHovered(true);
    const handleMouseOut = () => setHovered(false);
    const node = ref.current;
    if (node) {
      node.addEventListener('mouseover', handleMouseOver);
      node.addEventListener('mouseout', handleMouseOut);
    }
    return () => {
      if (node) {
        node.removeEventListener('mouseover', handleMouseOver);
        node.removeEventListener('mouseout', handleMouseOut);
      }
    };
  }, [ref]);

  return [ref, hovered];
}
Enter fullscreen mode Exit fullscreen mode
const [hoverRef, isHovered] = useHover();

Enter fullscreen mode Exit fullscreen mode

useDebounce

Ngăn chặn sự kích hoạt nhanh chóng của các sự kiện, đã sử dụng có thể hữu ích cho các trường đầu vào hoặc thanh tìm kiếm

import { useState, useEffect } from 'react';

function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => clearTimeout(handler);
  }, [value, delay]);

  return debouncedValue;
}
Enter fullscreen mode Exit fullscreen mode
const debouncedSearchTerm = useDebounce(searchTerm, 500);

Enter fullscreen mode Exit fullscreen mode

useInfiniteScroll

useInfiniteScroll hook rất hữu ích để tìm nạp dữ liệu khi người dùng scroll xuống. Nó có thể xử líu các state phúc tạp như loading, error handling, pagination...

import { useState, useEffect, useRef, useCallback } from 'react';

function useInfiniteScroll(fetchData, options = {}) {
  const { threshold = 0.8, hasMore = true } = options;
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const observerRef = useRef();

  const loadMore = useCallback(async () => {
    if (loading || !hasMore) return;
    setLoading(true);
    setError(null);
    try {
      const newData = await fetchData();
      setData((prevData) => [...prevData, ...newData]);
    } catch (err) {
      setError(err);
    } finally {
      setLoading(false);
    }
  }, [fetchData, loading, hasMore]);

  useEffect(() => {
    if (!hasMore) return;

    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0].isIntersecting) {
          loadMore();
        }
      },
      { threshold }
    );

    if (observerRef.current) observer.observe(observerRef.current);

    return () => {
      if (observerRef.current) observer.unobserve(observerRef.current);
    };
  }, [loadMore, hasMore, threshold]);

  return { data, loading, error, observerRef };
}
Enter fullscreen mode Exit fullscreen mode
function InfiniteScrollList() {
  const fetchMoreData = async () => {
    const response = await fetch('/api/data'); // Example API
    return response.json();
  };

  const { data, loading, error, observerRef } = useInfiniteScroll(fetchMoreData, {
    threshold: 0.9,
    hasMore: true
  });

  return (
    <div>
      {data.map((item, index) => (
        <div key={index}>{item}</div>
      ))}
      <div ref={observerRef} style={{ height: '1px' }} />
      {loading && <p>Loading...</p>}
      {error && <p>Error loading data...</p>}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read full post →

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more