React Native / useEffect React Native-д

useEffect React Native-д

useEffect нь React-н side effect удирдах hook — React Native-д яг адилхан ажилладаг. Апп нээгдэх үед өгөгдөл татах, дэлгэц өөрчлөгдөхөд ажиллах код, cleanup хийх — бүгд useEffect -ээр удирдагддаг. Энэ хичээлд мобайл аппд хамгийн их тохиолддог хэрэглээний загваруудыг судална.

useEffect үндэс — dependency array

useEffect нь хоёр аргументтай: ажиллуулах функц, болон dependency array (хамаарлын жагсаалт):

jsx
import { useEffect, useState } from "react";
import { View, Text, StyleSheet } from "react-native";

export default function App() {
  const [count, setCount] = useState(0);

  // 1. Dependency array байхгүй — render болох бүрт ажиллана
  useEffect(() => {
    console.log("Render боллоо");
  });

  // 2. Хоосон array [] — зөвхөн нэг удаа, component mount болоход
  useEffect(() => {
    console.log("Апп нээгдлээ — нэг удаа ажиллана");
  }, []);

  // 3. count-тай array — count өөрчлөгдөх бүрт ажиллана
  useEffect(() => {
    console.log("count өөрчлөгдлөө:", count);
  }, [count]);

  return (
    <View style={styles.container}>
      <Text style={styles.text}>count: {count}</Text>
      <Text style={styles.btn} onPress={() => setCount((c) => c + 1)}>
        +1
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "#0b1120",
  },
  text: { fontSize: 24, color: "#f1f5f9", marginBottom: 16 },
  btn: { fontSize: 20, color: "#22d3ee", padding: 12 },
});

Хоосон [] array нь хамгийн их хэрэглэгддэг хэлбэр — component нэг удаа render болоход өгөгдөл татах, timer эхлүүлэх зэрэгт ашиглана.

Дэлгэц нээгдэх үед өгөгдөл татах

Navigation-тэй хамт ашиглахад useFocusEffect hook байдаг боловч энгийн тохиолдолд useEffect + [] хангалттай:

jsx
import { useEffect, useState } from "react";
import {
  View,
  Text,
  FlatList,
  ActivityIndicator,
  StyleSheet,
} from "react-native";

export default function CourseListScreen() {
  const [courses, setCourses] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchCourses = async () => {
      try {
        const response = await fetch("https://api.example.com/courses");
        const data = await response.json();
        setCourses(data);
      } catch (err) {
        setError("Өгөгдөл татахад алдаа гарлаа.");
      } finally {
        setLoading(false);
      }
    };

    fetchCourses();
  }, []); // Зөвхөн нэг удаа, дэлгэц нээгдэх үед

  if (loading) {
    return (
      <View style={styles.center}>
        <ActivityIndicator size="large" color="#22d3ee" />
      </View>
    );
  }

  if (error) {
    return (
      <View style={styles.center}>
        <Text style={styles.errorText}>{error}</Text>
      </View>
    );
  }

  return (
    <FlatList
      data={courses}
      keyExtractor={(item) => item.slug}
      contentContainerStyle={styles.list}
      renderItem={({ item }) => (
        <View style={styles.card}>
          <Text style={styles.title}>{item.title}</Text>
        </View>
      )}
    />
  );
}

const styles = StyleSheet.create({
  center: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "#0b1120",
  },
  errorText: { color: "#fb7185", fontSize: 15 },
  list: { padding: 16, gap: 10, backgroundColor: "#0b1120", flexGrow: 1 },
  card: {
    backgroundColor: "#0f172a",
    padding: 16,
    borderRadius: 10,
    borderWidth: 1,
    borderColor: "#1e293b",
  },
  title: { color: "#f1f5f9", fontSize: 15 },
});

Cleanup функц — timer, subscription цэвэрлэх

useEffect -ын дотор функц буцаавал тэр нь cleanup функц болно. Component устах эсвэл dependency өөрчлөгдөх үед дуудагддаг:

jsx
import { useEffect, useState } from "react";
import { View, Text, StyleSheet } from "react-native";

export default function Timer() {
  const [seconds, setSeconds] = useState(0);
  const [running, setRunning] = useState(false);

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

    const interval = setInterval(() => {
      setSeconds((s) => s + 1);
    }, 1000);

    // Cleanup: running = false болоход эсвэл component устах үед interval цэвэрлэнэ
    return () => clearInterval(interval);
  }, [running]);

  return (
    <View style={styles.container}>
      <Text style={styles.time}>{seconds}с</Text>
      <Text style={styles.btn} onPress={() => setRunning((r) => !r)}>
        {running ? "Зогсоох" : "Эхлэх"}
      </Text>
      <Text
        style={styles.reset}
        onPress={() => {
          setRunning(false);
          setSeconds(0);
        }}
      >
        Дахин тохируулах
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "#0b1120",
    gap: 20,
  },
  time: { fontSize: 64, fontWeight: "bold", color: "#22d3ee" },
  btn: {
    fontSize: 18,
    color: "#f1f5f9",
    backgroundColor: "#1e293b",
    paddingHorizontal: 32,
    paddingVertical: 12,
    borderRadius: 10,
    overflow: "hidden",
  },
  reset: { fontSize: 14, color: "#475569" },
});

Cleanup функц мартах нь нийтлэг алдаа — timer, WebSocket, event listener бүгдийг цэвэрлэхгүй бол memory leak үүсдэг.

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

useEffect-ийн хэрэглээний загваруудыг сурлаа. Дараагийн хичээлд AsyncStorage — аппыг хаасны дараа ч өгөгдлийг хадгалах арга. Хэрэглэгчийн тохиргоо, нэвтрэлтийн мэдээлэл хадгалахад зайлшгүй хэрэгтэй.