Xử Lý Trạng Thái Tải Dữ Liệu và Lỗi (Loading/Error State) trong React

Tạo bởi Hoàng Vũ, chỉnh sửa cuối lúc 29 tháng 1, 2025

Trong các ứng dụng React, việc xử lý trạng thái tải dữ liệu và lỗi là rất quan trọng để đảm bảo giao diện người dùng phản hồi chính xác và mượt mà. Nếu không xử lý tốt, ứng dụng của bạn có thể gây nhầm lẫn cho người dùng hoặc khiến họ không biết trạng thái hiện tại của hệ thống. Bài viết này sẽ hướng dẫn bạn cách xử lý hiệu quả các trạng thái tải dữ liệu và lỗi.

1. Tại sao cần xử lý trạng thái tải dữ liệu và lỗi?

  • Cải thiện trải nghiệm người dùng (UX): Hiển thị trạng thái tải (loading) giúp người dùng biết rằng dữ liệu đang được tải thay vì nghĩ rằng ứng dụng bị treo.
  • Giảm thiểu nhầm lẫn: Trạng thái lỗi cung cấp thông tin rõ ràng khi có sự cố xảy ra, giúp người dùng hiểu và xử lý vấn đề dễ dàng hơn.
  • Tạo ứng dụng đáng tin cậy: Một giao diện phản hồi tốt cho phép người dùng cảm thấy an tâm khi sử dụng ứng dụng.

2. Các trạng thái cần quản lý

Khi tải dữ liệu từ API hoặc xử lý các tác vụ không đồng bộ khác, bạn thường phải quản lý các trạng thái sau:

  • Loading (Đang tải): Biểu thị dữ liệu đang được tải.
  • Error (Lỗi): Thông báo khi có vấn đề xảy ra trong quá trình tải dữ liệu.
  • Success (Thành công): Hiển thị dữ liệu khi tải xong.

3. Xử lý trạng thái cơ bản với React

Ví dụ: Tải dữ liệu và xử lý lỗi

Trong ví dụ này, chúng ta sẽ tải danh sách bài viết từ một API và xử lý các trạng thái tải và lỗi.

import React, { useState, useEffect } from "react";
import axios from "axios";

const DataFetchingComponent = () => {
  const [data, setData] = useState([]); // Lưu dữ liệu
  const [loading, setLoading] = useState(false); // Trạng thái tải
  const [error, setError] = useState(""); // Trạng thái lỗi

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true); // Bắt đầu tải
      setError(""); // Xóa lỗi cũ nếu có
      try {
        const response = await axios.get(
          "https://jsonplaceholder.typicode.com/posts"
        );
        setData(response.data); // Lưu dữ liệu vào state
      } catch (err) {
        setError("Đã xảy ra lỗi khi tải dữ liệu."); // Lưu trạng thái lỗi
      } finally {
        setLoading(false); // Kết thúc tải
      }
    };

    fetchData();
  }, []);

  if (loading) {
    return <p>Đang tải dữ liệu...</p>; // Hiển thị trạng thái tải
  }

  if (error) {
    return <p>{error}</p>; // Hiển thị lỗi nếu có
  }

  return (
    <ul>
      {data.map((item) => (
        <li key={item.id}>{item.title}</li>
      ))}
    </ul>
  );
};

export default DataFetchingComponent;

4. Tách riêng trạng thái tải và lỗi thành một hook

Để tái sử dụng code, bạn có thể tạo một custom hook để quản lý trạng thái tải và lỗi.

Custom Hook: useFetch

import { useState, useEffect } from "react";
import axios from "axios";

const useFetch = (url) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      setError("");
      try {
        const response = await axios.get(url);
        setData(response.data);
      } catch (err) {
        setError("Đã xảy ra lỗi khi tải dữ liệu.");
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
};

export default useFetch;

Sử dụng useFetch trong component

import React from "react";
import useFetch from "./useFetch";

const App = () => {
  const { data, loading, error } = useFetch(
    "https://jsonplaceholder.typicode.com/posts"
  );

  if (loading) return <p>Đang tải dữ liệu...</p>;
  if (error) return <p>{error}</p>;

  return (
    <ul>
      {data.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
};

export default App;

5. Cải thiện giao diện người dùng

Hiển thị Loading Spinner

Thay vì chỉ hiển thị một đoạn text đơn giản, bạn có thể dùng spinner để làm giao diện thêm sinh động.

const LoadingSpinner = () => (
  <div className="spinner">
    <div className="double-bounce1"></div>
    <div className="double-bounce2"></div>
  </div>
);

export default LoadingSpinner;

CSS cho spinner:

.spinner {
  width: 40px;
  height: 40px;
  position: relative;
  margin: auto;
}

.double-bounce1,
.double-bounce2 {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background-color: #3498db;
  opacity: 0.6;
  position: absolute;
  animation: bounce 2s infinite ease-in-out;
}

@keyframes bounce {
  0%, 100% {
    transform: scale(0);
  }
  50% {
    transform: scale(1);
  }
}

Hiển thị lỗi có thể tương tác

Cung cấp thêm nút "Thử lại" cho trạng thái lỗi:

if (error) {
  return (
    <div>
      <p>{error}</p>
      <button onClick={() => window.location.reload()}>Thử lại</button>
    </div>
  );
}

6. Kết luận

Việc xử lý trạng thái tải dữ liệu và lỗi trong React không chỉ giúp ứng dụng mượt mà hơn mà còn nâng cao trải nghiệm người dùng. Bằng cách tách riêng các trạng thái này và sử dụng custom hooks, bạn có thể giảm thiểu sự lặp lại trong code và dễ dàng tái sử dụng chúng trong các dự án.

Website Logo

Với hơn 10 năm kinh nghiệm lập trình web và từng làm việc với nhiều framework, ngôn ngữ như PHP, JavaScript, React, jQuery, CSS, HTML, CakePHP, Laravel..., tôi hy vọng những kiến thức được chia sẻ tại đây sẽ hữu ích và thiết thực cho các bạn.

Bình luận

Website Logo

Chào, tôi là Vũ. Đây là blog hướng dẫn lập trình của tôi.

Liên hệ công việc qua email dưới đây.

lhvuctu@gmail.com

Chúng Tôi Trên

Bạn đang muốn học về lập trình website?

Bạn cần nâng cao kiến thức chuyên nghiệp hơn để nâng cao cơ hội nghề nghiệp? Liên hệ