React / Error Boundary

Error Boundary

Апп ажиллаж байх үед component-д алдаа гарвал React бүх дэлгэцийг цагаан хоосон болгодог — хэрэглэгч юу болсныг ойлгохгүй гацдаг. Error Boundary нь алдааг "барьж" авч, хэрэглэгчид тааламжтай мэдэгдэл харуулдаг. Аппаа мэргэжлийн болгох маш чухал механизм!

Error Boundary гэж юу вэ?

Error Boundary нь child component-уудад гарсан JavaScript алдааг барьж авдаг тусгай component юм. Алдаа гарахад бүх аппыг унагааж дуусахын оронд зөвхөн тухайн хэсгийг "нуран унасан" байдлаар харуулж, бусад хэсгийг ажиллуулсаар байна.

jsx
// Error Boundary нь class component байх ёстой — функц component болохгүй
import { Component } from "react";

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

  // Алдаа гарахад энэ lifecycle method дуудагдана
  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }

  // Алдааг log хийх боломж
  componentDidCatch(error, info) {
    console.error("Алдаа гарлаа:", error);
    console.error("Component stack:", info.componentStack);
  }

  render() {
    if (this.state.hasError) {
      // Алдааны үед харуулах UI
      return (
        <div style={{ padding: "20px", color: "red", border: "1px solid red" }}>
          <h2>Ямар нэг зүйл буруу боллоо 😔</h2>
          <p>Хуудсыг дахин ачааллуулна уу.</p>
        </div>
      );
    }

    return this.props.children;
  }
}

export default ErrorBoundary;

Error Boundary нь class component байх ёстой — getDerivedStateFromError ба componentDidCatch lifecycle method-ууд зөвхөн class-д байдаг.

Error Boundary ашиглах нь

Error Boundary-г алдаа гарч болзошгүй component-уудаа боож хэрэглэнэ:

jsx
import ErrorBoundary from "./ErrorBoundary";

// Алдаа гаргадаг component — жишээ болгон
function BuggyCounter() {
  const [count, setCount] = useState(0);

  if (count === 3) {
    // 3 дарахад алдаа гарна
    throw new Error("Тоолуур гацлаа!");
  }

  return (
    <button onClick={() => setCount(count + 1)}>{count} удаа дарлаа</button>
  );
}

export default function App() {
  return (
    <div>
      <h1>Миний апп</h1>

      {/* Нэг хэсгийн алдаа нөгөөд нөлөөлөхгүй */}
      <ErrorBoundary>
        <BuggyCounter />
      </ErrorBoundary>

      <ErrorBoundary>
        <BuggyCounter />
      </ErrorBoundary>

      <p>Энэ хэсэг хэзээ ч алдаагүй ажиллана.</p>
    </div>
  );
}

Нэг BuggyCounter-д алдаа гарахад нөгөө нь хэвийн ажиллаж үлдэнэ. Error Boundary нь алдааг тусгаарладаг.

Дахин оролдох товч нэмэх

Хэрэглэгчид алдааны дараа дахин оролдох боломж өгөх нь UX сайжруулдаг:

jsx
class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
    this.handleReset = this.handleReset.bind(this);
  }

  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }

  componentDidCatch(error, info) {
    // Жинхэнэ аппад энд Sentry эсвэл бусад log сервис рүү илгээнэ
    console.error(error, info);
  }

  handleReset() {
    this.setState({ hasError: false, error: null });
  }

  render() {
    if (this.state.hasError) {
      return (
        <div
          style={{
            padding: "32px",
            textAlign: "center",
            border: "1px solid #fee2e2",
            borderRadius: "8px",
            background: "#fef2f2",
          }}
        >
          <h2>Алдаа гарлаа 😕</h2>
          <p style={{ color: "#6b7280" }}>
            {this.state.error?.message ?? "Тодорхойгүй алдаа"}
          </p>
          <button
            onClick={this.handleReset}
            style={{
              marginTop: "16px",
              padding: "8px 16px",
              background: "#3b82f6",
              color: "white",
              border: "none",
              borderRadius: "6px",
              cursor: "pointer",
            }}
          >
            Дахин оролдох
          </button>
        </div>
      );
    }

    return this.props.children;
  }
}

Error Boundary юу барьж чадахгүй вэ?

Error Boundary барьж чаддаггүй алдаанууд байдаг — эдгээрийг мэдэх нь чухал:

jsx
// ❌ Event handler дахь алдааг барьдаггүй
function BadButton() {
  const handleClick = () => {
    throw new Error("Click алдаа"); // Error Boundary энийг барихгүй!
  };
  return <button onClick={handleClick}>Дарах</button>;
}

// ✅ Event handler дахь алдааг try/catch-ээр барина
function GoodButton() {
  const [error, setError] = useState(null);
  const handleClick = () => {
    try {
      throw new Error("Click алдаа");
    } catch (err) {
      setError(err.message);
    }
  };
  return (
    <div>
      <button onClick={handleClick}>Дарах</button>
      {error && <p style={{ color: "red" }}>{error}</p>}
    </div>
  );
}

Error Boundary нь зөвхөн render, lifecycle method, constructor дахь алдааг барьдаг. Async функц, event handler дахь алдааг тусад нь try/catch-ээр зохицуулах хэрэгтэй.

Дараагийн хичээлд:

Suspense ба lazy loading сурна — том component-уудыг шаардлагатай үед л ачаалах арга. Энэ нь аппын эхний ачааллын хугацааг эрс богиносгодог чухал оновчлол юм.