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

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (0)

Billboard image

Create up to 10 Postgres Databases on Neon's free plan.

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Try Neon for Free →

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay