• PEMROGRAMAN WEB DINAMIS

    Pengertian web dinamis adalah suatu web yang konten atau isinya dapat berubah-ubah setiap saat. Sebab dalam teknologi pembuatan web dinamis sudah dirancang semudah mungkin bagi pemakai atau user yang menggunakan web tersebut..

  • SIMULASI DAN KOMUNIKASI DIGITAL

    Suatu proses peniruan dalam bentuk visual yang dideskripsikan menyerupai kata, gambar dan grafis..

  • SISTEM KOMPUTER

    Sistem komputer adalah suatu jaringan elektronik yang terdiri dari perangkat lunak dan perangkat keras yang melakukan tugas tertentu (menerima input, memproses input, menyimpan perintah-perintah, dan menyediakan output dalam bentuk informasi). Selain itu dapat pula diartikan sebagai elemen-elemen yang terkait untuk menjalankan suatu aktivitas dengan menggunakan komputer..

  • DASAR DESAIN GRAPIS

    Banyak yang berpikiran kalau desain yang baik adalah yang membutuhkan jam kerja yang banyak, membutuhkan skill tinggi dan aplikasi yang mahal. Ya, memang, tapi sebenarnya desain yang baik adalah desain yang sederhana, yang membuat setiap orang yang melihatnya mudah menangkap maksud dari sebuah bentuk visual tersebut..

  • BASIS DATA

    Pangkalan data atau basis data (bahasa Inggris: database) adalah kumpulan informasi yang disimpan di dalam komputer secara sistematik sehingga dapat diperiksa menggunakan suatu program komputer untuk memperoleh informasi dari basis data tersebut. Perangkat lunak yang digunakan untuk mengelola dan memanggil kueri (query) basis data disebut sistem manajemen basis data (database management system, DBMS). Sistem basis data dipelajari dalam ilmu informasi.

🧑‍🏫 PEMBELAJARAN – REACT NATIVE STATE & HOOKS DASAR


🧑‍🏫 PEMBELAJARAN – REACT NATIVE STATE & HOOKS DASAR


Halo, anak-anakku yang hebat! 👋

Maaf hari ini saya tidak bisa hadir menemani kalian di kelas. Tapi jangan khawatir, saya sudah menyusun panduan belajar mandiri yang terarah untuk React Native (mobile).

Tugas kalian hari ini:
1️⃣ Memahami konsep State dan Hooks di React Native.
2️⃣ Membuat 1 Project Aplikasi Mobile (Counter + Timer).
3️⃣ Mendokumentasikan proses belajar dalam postingan blog dan mengirimkan link-nya ke kolom komentar di postingan ini.


🎯 TUJUAN PEMBELAJARAN

Setelah mengikuti pembelajaran ini, kalian diharapkan mampu:

  • Menjelaskan fungsi useState dan useEffect dalam React Native.

  • Mengelola data dinamis (state) pada komponen fungsional.

  • Menangani efek samping (side effects) seperti timer dan interval di aplikasi mobile.

  • Membangun aplikasi mobile sederhana dengan interaksi pengguna.

  • Mendokumentasikan proses belajar secara tertulis.


📚 MATERI INTI

React Native menggunakan Hooks yang sama persis dengan React untuk web. Perbedaannya hanya pada komponen UI yang digunakan (menggunakan komponen native seperti View, Text, Button dari react-native) .

1. State Management dengan useState

State adalah data yang dapat berubah dan mempengaruhi tampilan komponen. Di React Native, kita menggunakan Hook useState dengan cara yang sama seperti di React .

Sintaks dasar:

jsx
const [nilai, setNilai] = useState(initialValue);
  • nilai → state saat ini.

  • setNilai → fungsi untuk mengubah state.

  • initialValue → nilai awal.

Contoh penggunaan di React Native:

jsx
import React, { useState } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';

const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <View style={styles.container}>
      <Text style={styles.countText}>Count: {count}</Text>
      <Button title="Tambah" onPress={() => setCount(count + 1)} />
      <Button title="Kurang" onPress={() => setCount(count - 1)} />
      <Button title="Reset" onPress={() => setCount(0)} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
  countText: { fontSize: 24, marginBottom: 20 }
});

export default Counter;

Setiap kali setCount dipanggil, komponen akan di-render ulang dengan nilai state terbaru .


2. Efek Samping dengan useEffect

useEffect digunakan untuk menjalankan kode saat komponen di-render, di-update, atau dihapus dari layar. Cocok untuk:

  • Mengambil data dari API.

  • Mengatur interval/timer.

  • Membersihkan resource (cleanup) .

Sintaks dasar:

jsx
useEffect(() => {
  // kode efek samping
  return () => {
    // kode cleanup (opsional)
  };
}, [dependencies]);
  • [] kosong → efek dijalankan sekali saat komponen pertama kali tampil.

  • [variabel] → efek dijalankan setiap kali variabel berubah.

  • Tanpa array kedua → efek dijalankan setiap kali komponen di-render .

Contoh Timer di React Native:

jsx
import React, { useState, useEffect } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';

const Timer = () => {
  const [detik, setDetik] = useState(0);
  const [berjalan, setBerjalan] = useState(false);

  useEffect(() => {
    let interval = null;
    if (berjalan) {
      interval = setInterval(() => {
        setDetik(prev => prev + 1);
      }, 1000);
    } else {
      clearInterval(interval);
    }
    return () => clearInterval(interval); // cleanup
  }, [berjalan]);

  return (
    <View style={styles.container}>
      <Text style={styles.timerText}>⏱️ {detik} detik</Text>
      <View style={styles.buttonRow}>
        <Button title="Mulai" onPress={() => setBerjalan(true)} />
        <Button title="Stop" onPress={() => setBerjalan(false)} />
        <Button title="Reset" onPress={() => setDetik(0)} />
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
  timerText: { fontSize: 32, marginBottom: 20 },
  buttonRow: { flexDirection: 'row', gap: 10 }
});

export default Timer;

🔍 Catatan penting:
Cleanup function (return () => clearInterval(interval)) mencegah memory leak saat komponen di-unmount .


🧪 PERINTAH PRAKTIKUM (IKUTI STEP BY STEP)

🚀 Persiapan Project

Pilih salah satu cara berikut:

Opsi A: Menggunakan Expo (Direkomendasikan untuk pemula)

bash
npx create-expo-app CounterTimerApp
cd CounterTimerApp
npm start

Opsi B: Menggunakan React Native CLI

bash
npx @react-native-community/cli@latest init CounterTimerApp
cd CounterTimerApp
npm start

Untuk menjalankan di emulator:

bash
npm run android   # untuk Android
npm run ios       # untuk iOS

  1. Buka folder project di VS Code atau editor favorit kalian.


📝 STEP 1: Membuat Komponen Counter (Gunakan useState)

  • Buat file Counter.jsx di folder src atau di root project (sesuai struktur).

  • Buat komponen dengan:

    • Tampilan angka (state count).

    • Tombol + dan untuk mengubah count (gunakan Button dari react-native).

    • Tombol Reset untuk mengembalikan ke 0.

  • Export dan import ke App.jsx, lalu tampilkan.

Boleh lihat cuplikan di materi inti sebagai referensi.


⏱️ STEP 2: Membuat Komponen Timer (Gunakan useEffect)

  • Buat file Timer.jsx.

  • Buat komponen dengan:

    • Tampilkan detik (state seconds).

    • Tombol Mulai, Stop, dan Reset.

    • Gunakan useEffect dan setInterval untuk update detik setiap 1 detik.

    • Jangan lupa cleanup interval!

  • Export dan import ke App.jsx, tampilkan di bawah Counter.


🔗 STEP 3: Integrasi & Fitur Tambahan (Wajib)

Gabungkan Counter dan Timer dalam satu halaman dengan layout sederhana menggunakan View dan ScrollView.

Tambahkan minimal 1 fitur interaksi antar komponen, contoh:

  • Saat counter mencapai 0 (nol), timer otomatis berhenti.

  • Atau saat timer mencapai 10 detik, counter otomatis bertambah 1.

  • Simpan state ke AsyncStorage agar tetap bertahan saat aplikasi ditutup.

💡 Pilih salah satu atau buat ide kalian sendiri yang menarik!


🎨 STEP 4: Styling (Opsional tapi nilai tambah)

Beri tampilan yang rapi. Kalian bisa pakai:

  • StyleSheet dari React Native (cara standar).

  • NativeWind (Tailwind untuk React Native) .

  • Library UI seperti react-native-elements.


📝 STEP 5: Dokumentasi Blog (WAJIB)

Buat postingan blog di platform pilihan kalian, misalnya:

Isi blog minimal:

  1. Judul menarik (contoh: "Belajar useState dan useEffect di React Native – Membuat Counter & Timer Mobile").

  2. Penjelasan singkat tentang useState dan useEffect dengan bahasa kalian sendiri.

  3. Cuplikan kode dari Counter dan Timer (jangan copy-paste semua, pilih bagian penting).

  4. Screenshot tampilan aplikasi yang sudah jadi di emulator atau HP.

  5. Kendala yang kalian hadapi dan bagaimana cara mengatasinya.

  6. Kesimpulan – apa yang kalian pelajari hari ini.


📤 STEP 6: Submit

  1. Copy URL blog kalian.

  2. Tempelkan link tersebut di kolom komentar pada postingan ini.

  3. Sertakan Nama dan Kelas di komentar.


📎 SUMBER BELAJAR PENDUKUNG


✅ KRITERIA PENILAIAN

Aspek yang DinilaiBobot
Kode berjalan tanpa error dan sesuai fitur30%
Pemahaman konsep (terlihat dari blog & kode)25%
Kreativitas fitur tambahan15%
Kerapihan kode dan struktur project10%
Dokumentasi blog (kelengkapan & kualitas)20%

Referensi lain:

Kita buat versi React Native yang modern, clean, dan cocok dijadikan project pembelajaran siswa. Konsepnya bukan sekadar Counter + Timer, tetapi keduanya benar-benar terhubung.

🎯 Konsep aplikasi: FOCUS COUNTER

Fitur:

  • 🔢 Counter +, , Reset
  • ⏱️ Timer Mulai, Stop, Reset
  • 🔗 Counter & Timer saling terhubung
  • Jika counter kembali ke 0 → timer otomatis berhenti
  • Saat counter bertambah → timer dapat berjalan
  • ✨ UI modern dengan gradient-style visual, card, status indicator, dan animasi sederhana
  • 📱 Responsive untuk Android
  • 🧹 useEffect digunakan untuk mengelola interval dan cleanup

1. Buat project React Native

Kalau menggunakan Expo:

npx create-expo-app FocusCounter
cd FocusCounter
npx expo start

Kemudian buka project di VS Code.

Ganti isi App.js dengan kode berikut:

import React, { useEffect, useRef, useState } from "react";
import {
  SafeAreaView,
  View,
  Text,
  TouchableOpacity,
  StyleSheet,
  StatusBar,
} from "react-native";

export default function App() {
  // =========================
  // STATE COUNTER
  // =========================
  const [counter, setCounter] = useState(0);

  // =========================
  // STATE TIMER
  // =========================
  const [seconds, setSeconds] = useState(0);
  const [isRunning, setIsRunning] = useState(false);

  const intervalRef = useRef(null);

  // =========================
  // FORMAT TIMER
  // =========================
  const formatTime = (totalSeconds) => {
    const minutes = Math.floor(totalSeconds / 60);
    const secs = totalSeconds % 60;

    return `${String(minutes).padStart(2, "0")}:${String(
      secs
    ).padStart(2, "0")}`;
  };

  // =========================
  // USE EFFECT TIMER
  // =========================
  useEffect(() => {
    if (isRunning) {
      intervalRef.current = setInterval(() => {
        setSeconds((prev) => prev + 1);
      }, 1000);
    }

    return () => {
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
        intervalRef.current = null;
      }
    };
  }, [isRunning]);

  // =========================
  // HUBUNGKAN COUNTER & TIMER
  // COUNTER 0 = TIMER STOP
  // =========================
  useEffect(() => {
    if (counter === 0) {
      setIsRunning(false);
    }
  }, [counter]);

  // =========================
  // COUNTER FUNCTION
  // =========================
  const increaseCounter = () => {
    setCounter((prev) => prev + 1);
  };

  const decreaseCounter = () => {
    setCounter((prev) => Math.max(0, prev - 1));
  };

  const resetCounter = () => {
    setCounter(0);
  };

  // =========================
  // TIMER FUNCTION
  // =========================
  const startTimer = () => {
    if (counter > 0) {
      setIsRunning(true);
    }
  };

  const stopTimer = () => {
    setIsRunning(false);
  };

  const resetTimer = () => {
    setIsRunning(false);
    setSeconds(0);
  };

  // =========================
  // RESET SEMUA
  // =========================
  const resetAll = () => {
    setCounter(0);
    setSeconds(0);
    setIsRunning(false);
  };

  return (
    <SafeAreaView style={styles.container}>
      <StatusBar barStyle="light-content" />

      {/* HEADER */}
      <View style={styles.header}>
        <View>
          <Text style={styles.smallTitle}>PRODUCTIVITY APP</Text>
          <Text style={styles.title}>Focus Counter</Text>
        </View>

        <View style={styles.statusBadge}>
          <View
            style={[
              styles.statusDot,
              isRunning && styles.statusDotActive,
            ]}
          />
          <Text style={styles.statusText}>
            {isRunning ? "RUNNING" : "IDLE"}
          </Text>
        </View>
      </View>

      {/* TIMER CARD */}
      <View style={styles.timerCard}>
        <Text style={styles.cardLabel}>FOCUS TIMER</Text>

        <Text style={styles.timerText}>
          {formatTime(seconds)}
        </Text>

        <Text style={styles.timerDescription}>
          {counter === 0
            ? "Tambah counter untuk memulai fokus"
            : isRunning
            ? "Timer sedang berjalan..."
            : "Timer siap digunakan"}
        </Text>

        <View style={styles.timerButtons}>
          <TouchableOpacity
            style={[
              styles.primaryButton,
              counter === 0 && styles.disabledButton,
            ]}
            onPress={startTimer}
            disabled={counter === 0}
          >
            <Text style={styles.primaryButtonText}>▶ Mulai</Text>
          </TouchableOpacity>

          <TouchableOpacity
            style={styles.secondaryButton}
            onPress={stopTimer}
          >
            <Text style={styles.secondaryButtonText}>Ⅱ Stop</Text>
          </TouchableOpacity>

          <TouchableOpacity
            style={styles.secondaryButton}
            onPress={resetTimer}
          >
            <Text style={styles.secondaryButtonText}>↻ Reset</Text>
          </TouchableOpacity>
        </View>
      </View>

      {/* COUNTER CARD */}
      <View style={styles.counterCard}>
        <View style={styles.cardHeader}>
          <View>
            <Text style={styles.cardLabel}>ACTIVITY COUNTER</Text>
            <Text style={styles.counterSubtitle}>
              Total aktivitas hari ini
            </Text>
          </View>

          <View style={styles.counterIcon}>
            <Text style={styles.iconText}></Text>
          </View>
        </View>

        <Text style={styles.counterNumber}>{counter}</Text>

        <View style={styles.counterButtons}>
          <TouchableOpacity
            style={styles.minusButton}
            onPress={decreaseCounter}
          >
            <Text style={styles.bigButtonText}></Text>
          </TouchableOpacity>

          <TouchableOpacity
            style={styles.plusButton}
            onPress={increaseCounter}
          >
            <Text style={styles.bigButtonText}></Text>
          </TouchableOpacity>
        </View>

        <TouchableOpacity
          style={styles.resetCounterButton}
          onPress={resetCounter}
        >
          <Text style={styles.resetCounterText}>
            Reset Counter
          </Text>
        </TouchableOpacity>
      </View>

      {/* CONNECTION INFO */}
      <View style={styles.connectionCard}>
        <View style={styles.connectionIcon}>
          <Text>🔗</Text>
        </View>

        <View style={styles.connectionContent}>
          <Text style={styles.connectionTitle}>
            Smart Connection
          </Text>

          <Text style={styles.connectionText}>
            {counter === 0
              ? "Counter = 0 → Timer otomatis berhenti"
              : "Counter aktif → Timer siap digunakan"}
          </Text>
        </View>
      </View>

      {/* FOOTER */}
      <View style={styles.footer}>
        <Text style={styles.footerText}>
          React Native • useState • useEffect
        </Text>

        <TouchableOpacity onPress={resetAll}>
          <Text style={styles.resetAllText}>Reset All</Text>
        </TouchableOpacity>
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#0B1020",
    paddingHorizontal: 20,
    paddingTop: 20,
  },

  // HEADER
  header: {
    flexDirection: "row",
    justifyContent: "space-between",
    alignItems: "center",
    marginBottom: 20,
  },

  smallTitle: {
    color: "#7C8DB5",
    fontSize: 11,
    fontWeight: "700",
    letterSpacing: 2,
  },

  title: {
    color: "#FFFFFF",
    fontSize: 28,
    fontWeight: "800",
    marginTop: 3,
  },

  statusBadge: {
    flexDirection: "row",
    alignItems: "center",
    backgroundColor: "#151D35",
    paddingHorizontal: 12,
    paddingVertical: 8,
    borderRadius: 20,
  },

  statusDot: {
    width: 8,
    height: 8,
    borderRadius: 4,
    backgroundColor: "#59637D",
    marginRight: 7,
  },

  statusDotActive: {
    backgroundColor: "#34D399",
  },

  statusText: {
    color: "#AAB4CF",
    fontSize: 10,
    fontWeight: "800",
  },

  // TIMER
  timerCard: {
    backgroundColor: "#151D35",
    borderRadius: 28,
    padding: 24,
    marginBottom: 16,
    borderWidth: 1,
    borderColor: "#222D4C",
  },

  cardLabel: {
    color: "#7C8DB5",
    fontSize: 11,
    fontWeight: "800",
    letterSpacing: 1.5,
  },

  timerText: {
    color: "#FFFFFF",
    fontSize: 64,
    fontWeight: "800",
    textAlign: "center",
    marginTop: 15,
    letterSpacing: 2,
  },

  timerDescription: {
    color: "#8995B2",
    textAlign: "center",
    fontSize: 13,
    marginBottom: 22,
  },

  timerButtons: {
    flexDirection: "row",
    gap: 8,
  },

  primaryButton: {
    flex: 1,
    backgroundColor: "#6C63FF",
    paddingVertical: 14,
    borderRadius: 15,
    alignItems: "center",
  },

  disabledButton: {
    opacity: 0.35,
  },

  primaryButtonText: {
    color: "#FFFFFF",
    fontWeight: "800",
    fontSize: 13,
  },

  secondaryButton: {
    flex: 1,
    backgroundColor: "#202A47",
    paddingVertical: 14,
    borderRadius: 15,
    alignItems: "center",
  },

  secondaryButtonText: {
    color: "#C8D0E3",
    fontWeight: "700",
    fontSize: 13,
  },

  // COUNTER
  counterCard: {
    backgroundColor: "#151D35",
    borderRadius: 28,
    padding: 24,
    marginBottom: 16,
    borderWidth: 1,
    borderColor: "#222D4C",
  },

  cardHeader: {
    flexDirection: "row",
    justifyContent: "space-between",
    alignItems: "center",
  },

  counterSubtitle: {
    color: "#687593",
    fontSize: 12,
    marginTop: 4,
  },

  counterIcon: {
    width: 42,
    height: 42,
    borderRadius: 14,
    backgroundColor: "#29264D",
    justifyContent: "center",
    alignItems: "center",
  },

  iconText: {
    fontSize: 20,
  },

  counterNumber: {
    color: "#FFFFFF",
    fontSize: 72,
    fontWeight: "900",
    textAlign: "center",
    marginVertical: 12,
  },

  counterButtons: {
    flexDirection: "row",
    gap: 12,
  },

  minusButton: {
    flex: 1,
    height: 58,
    backgroundColor: "#202A47",
    borderRadius: 18,
    justifyContent: "center",
    alignItems: "center",
  },

  plusButton: {
    flex: 1,
    height: 58,
    backgroundColor: "#6C63FF",
    borderRadius: 18,
    justifyContent: "center",
    alignItems: "center",
  },

  bigButtonText: {
    color: "#FFFFFF",
    fontSize: 30,
    fontWeight: "700",
  },

  resetCounterButton: {
    alignItems: "center",
    paddingVertical: 15,
  },

  resetCounterText: {
    color: "#8792AD",
    fontSize: 12,
    fontWeight: "700",
  },

  // CONNECTION
  connectionCard: {
    flexDirection: "row",
    alignItems: "center",
    backgroundColor: "#11182C",
    borderRadius: 20,
    padding: 16,
    borderWidth: 1,
    borderColor: "#202A47",
  },

  connectionIcon: {
    width: 42,
    height: 42,
    borderRadius: 14,
    backgroundColor: "#202A47",
    justifyContent: "center",
    alignItems: "center",
    marginRight: 12,
  },

  connectionContent: {
    flex: 1,
  },

  connectionTitle: {
    color: "#FFFFFF",
    fontSize: 13,
    fontWeight: "800",
    marginBottom: 4,
  },

  connectionText: {
    color: "#7F8AA5",
    fontSize: 11,
    lineHeight: 16,
  },

  // FOOTER
  footer: {
    flexDirection: "row",
    justifyContent: "space-between",
    marginTop: "auto",
    paddingVertical: 15,
  },

  footerText: {
    color: "#4F5A75",
    fontSize: 10,
  },

  resetAllText: {
    color: "#6C63FF",
    fontSize: 10,
    fontWeight: "800",
  },
});

🧠 Bagian penting untuk dipelajari siswa

1. useState untuk Counter

const [counter, setCounter] = useState(0);

Ketika tombol + ditekan:

setCounter((prev) => prev + 1);

Ketika tombol -:

setCounter((prev) => Math.max(0, prev - 1));

Kenapa memakai Math.max(0, ...)?

Supaya counter tidak bisa menjadi angka negatif.


2. useState untuk Timer

const [seconds, setSeconds] = useState(0);
const [isRunning, setIsRunning] = useState(false);

seconds menyimpan waktu.

isRunning menentukan apakah timer sedang berjalan atau tidak.


3. useEffect sebagai mesin Timer

Bagian ini yang paling penting:

useEffect(() => {
  if (isRunning) {
    intervalRef.current = setInterval(() => {
      setSeconds((prev) => prev + 1);
    }, 1000);
  }

  return () => {
    if (intervalRef.current) {
      clearInterval(intervalRef.current);
      intervalRef.current = null;
    }
  };
}, [isRunning]);

Ketika:

isRunning = true
        ↓
setInterval()
        ↓
setiap 1 detik
        ↓
seconds + 1

Ketika:

isRunning = false
        ↓
clearInterval()
        ↓
timer berhenti

return di dalam useEffect sangat penting karena berfungsi sebagai cleanup sehingga interval lama tidak terus berjalan.


🔗 4. Hubungan Counter dengan Timer

Ini bagian yang membuat project lebih menarik daripada Counter + Timer biasa:

useEffect(() => {
  if (counter === 0) {
    setIsRunning(false);
  }
}, [counter]);

Artinya:

COUNTER
   │
   ├── 0
   │    ↓
   │ TIMER STOP
   │
   └── > 0
        ↓
     TIMER SIAP

Contoh:

Counter = 0

Timer tidak dapat dimulai.

Tekan +

Counter = 1

Timer siap dimulai.

Tekan Mulai

Timer = 00:01 → 00:02 → 00:03 ...

Tekan -

Counter = 0

Timer otomatis STOP. 🔗


🚀 Ide pengembangan versi 2

Kalau ini dijadikan project praktikum siswa SMKN 1 Banjar, saya sarankan jangan berhenti di versi dasar. Bisa dibuat bertahap:

Level 1 — Basic

  • Counter
  • Timer
  • useState
  • useEffect

Level 2 — UI/UX

  • Dark mode
  • Animasi tombol
  • Progress indicator
  • Status RUNNING / IDLE
  • Card modern

Level 3 — Smart App

  • Counter menentukan durasi timer
  • Misalnya Counter = 5 → target timer 5 menit
  • Progress timer
  • Notifikasi ketika selesai

Level 4 — Challenge

  • Tambahkan Pomodoro Mode
  • Riwayat aktivitas
  • Total sesi hari ini
  • Statistik produktivitas
  • AsyncStorage untuk menyimpan data
  • Sound ketika timer selesai

Dengan begitu siswa tidak hanya belajar useState dan useEffect, tetapi juga memahami state management, side effect, cleanup, event handling, conditional rendering, dan UI/UX React Native.



💬 PESAN :

Anak-anak, saya percaya kalian semua mampu menyelesaikan tugas ini dengan baik.
Jangan takut mencoba, jangan malu bertanya di grup diskusi.
Setiap error adalah bagian dari proses belajar.
Yang penting bukan hasil sempurna, tapi usaha dan pemahaman yang kalian dapat.

Semangat! Saya tunggu link blog kalian di kolom komentar. 🔥


Salam sukses,
Darsu.

Share:

Chat Admin

Chat via WhatsApp

Categories

Blogger Tricks

Blogger Themes