Ось готовий урок у стилі David Malan / CS50, створений спеціально для тебе. Пристебни паски безпеки, ми занурюємось у світ React Hooks! 🚀
🎓 CS50: React Hooks — Магія useState та useEffect
Привіт, світе! 👋 Це CS50, і сьогодні ми розберемо тему, яка перетворює "мертвий" код на живий, інтерактивний організм.
Ми говоримо про Хуки (Hooks). Зокрема, про двох китів, на яких тримається сучасний React: useState та useEffect.
1. 🔥 Вступ: Проблема "Амнезії" Функцій
Уявіть собі ситуацію. Ви приходите в кав’ярню, замовляєте капучино. Бариста робить каву, віддає вам, ви йдете. Ви повертаєтесь через хвилину і кажете: "Я хочу ще цукру". А бариста дивиться на вас порожніми очима і питає: "А ви хто?".
Проблема: Звичайні функції в JavaScript мають амнезію. Як тільки функція виконалася (відмалювала компонент) і завершила роботу — вона забуває все, що всередині неї відбувалося. Всі змінні знищуються.
Але зачекайте! ✋ Коли ми пишемо додаток (наприклад, Instagram), і ви ставите лайк ❤️, додаток має запам'ятати, що лайк стоїть, навіть якщо ви проскролили стрічку туди-сюди.
Тут виникає два питання: 1. Як змусити функцію "пам'ятати" дані між оновленнями екрана? 2. Як змусити функцію реагувати на зміни (наприклад, завантажити нові пости, коли ви догортали до низу)?
Без цього ваш сайт — це просто красива, але дурна картинка. Хуки — це імпланти пам'яті та реакції для ваших функцій.
2. 🧠 Теоретична база (Що під капотом?)
Давайте розберемо це без складних слів, як для друзів.
📍 1. useState — Це рюкзак пам'яті
Уявіть, що ваш компонент — це мандрівник. Кожного разу, коли React оновлює екран, мандрівник перероджується (функція запускається заново). Але useState дає йому рюкзак, який передається від минулої версії мандрівника до нової.
Синтаксис:
const [count, setCount] = useState(0);
count(змінна): Те, що лежить у рюкзаку зараз.setCount(функція): Єдиний спосіб покласти щось нове в рюкзак.0(аргумент): Що покласти в рюкзак на самому початку (початкове значення).
👉 Важливо зрозуміти: Ви не можете просто написати count = 5. React про це не дізнається. Тільки setCount(5) "смикає" React і каже: "Ей! Дані змінилися! Перемалюй екран!".
📍 2. useEffect — Це ваш особистий асистент
Якщо useState — це пам'ять, то useEffect — це дії, які виходять за межі простого відображення HTML. Це побічні ефекти (side effects).
Аналогія: Ви (компонент) зайшли в кімнату (відрендерились). Що далі? * Може, треба увімкнути кондиціонер? (Запит на сервер). * Може, треба підписатися на газету? (Event Listener). * Може, треба змінити заголовок сторінки?
Як це працює:
useEffect(() => {
// Дія, яку треба зробити
}, [ /* Масив залежностей */ ]);
Масив залежностей (Dependency Array) — це найважливіше! Це пульт керування асистентом:
* [] (порожній): "Зроби це один раз, коли я тільки з'явився (mount)".
* [count]: "Роби це кожного разу, коли змінюється змінна count".
* (без масиву): "Роби це кожного разу, коли щось відбувається" (Обережно! Це може покласти ваш браузер).
3. 🧪 Приклади (Step-by-Step)
Приклад 1: Лічильник лайків (useState)
Давайте почнемо з бази. Ми хочемо кнопку, яка рахує кліки.
❓ Питання до вас: Що буде, якщо ми просто створимо змінну let likes = 0 всередині компонента і будемо її збільшувати?
(Спойлер: На екрані нічого не зміниться, бо React не стежить за звичайними змінними).
Ось як правильно:
import { useState } from 'react';
function LikeButton() {
// 1. Оголошуємо стан
const [likes, setLikes] = useState(0);
function handleClick() {
// 2. Змінюємо стан
setLikes(likes + 1);
}
return (
<button onClick={handleClick}>
❤️ Лайків: {likes}
</button>
);
}
Результат: Кожен клік змушує React запустити функцію LikeButton знову, але likes тепер буде 1, потім 2, і так далі.
Приклад 2: "Шпигун" за заголовком (useEffect)
Тепер уявімо, що ми хочемо, щоб кількість лайків відображалася у вкладці браузера (document.title).
Це зміна зовнішнього світу (браузера), а не самого React-компонента. Це робота для useEffect.
import { useState, useEffect } from 'react';
function LikeSpy() {
const [likes, setLikes] = useState(0);
// Цей код спрацьовує КОЖЕН раз, коли змінюється likes
useEffect(() => {
document.title = `Лайків: ${likes}`;
console.log("Заголовок оновлено!");
}, [likes]); // <--- Дивіться на масив залежностей!
return (
<button onClick={() => setLikes(likes + 1)}>
Клікни мене ({likes})
</button>
);
}
Чому так? Ми сказали React: "Слухай, як тільки зміниться змінна likes, будь ласка, піди і онови заголовок вкладки".
Приклад 3: Таймер зі зворотним відліком (Складніше + Cleanup)
А що, якщо ми пішли з кімнати (видалили компонент), а асистент продовжує працювати? Це витік пам'яті!
Нам потрібна функція очищення (cleanup function). Це як вимкнути світло перед виходом.
import { useState, useEffect } from 'react';
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
// Запускаємо інтервал
const intervalId = setInterval(() => {
setSeconds(s => s + 1);
console.log('Тік-так...');
}, 1000);
// 🧹 Функція очищення (спрацює, коли компонент зникне)
return () => {
clearInterval(intervalId);
console.log('Таймер зупинено!');
};
}, []); // Порожній масив = запускаємо 1 раз на старті
return <h1>Минуло секунд: {seconds}</h1>;
}
4. 🛠 Практична частина
Час забруднити руки кодом! Відкривайте редактор (або CodeSandbox).
🔹 Завдання 1: Input Mirror
Створіть input. Використовуйте useState, щоб текст, який ви вводите, одразу відображався в параграфі <p> під інпутом.
🔹 Завдання 2: Toggle (Перемикач)
Зробіть кнопку "Показати/Приховати". При кліку текст під нею має зникати або з'являтися. (Підказка: useState(true) або false, і умовний рендеринг show && <Text />).
🔹 Завдання 3: Виправити баг
Студент написав такий код для useEffect, але він створює нескінченний цикл і вішає браузер. Чому? Виправте це.
useEffect(() => {
setCount(count + 1); // Ой-ой...
}); // Чого тут не вистачає?
🔹 Завдання 4: Міні-кейс "Курс валют"
Зробіть компонент, який при завантаженні (один раз!) виводить у консоль "Отримання даних...", а через 2 секунди (використайте setTimeout як імітацію запиту) змінює текст на екрані з "Завантаження..." на "1 USD = 41 UAH".
🔹 Завдання 5: А що, якщо...
Що станеться, якщо у масиві залежностей useEffect вказати змінну, яка ніколи не змінюється? Перевірте свою гіпотезу.
5. 💡 Мислення як у розробника
Як думає Senior Developer, коли працює з хуками?
-
"Чи потрібно це в стейті?" Новачки пхають у
useStateвсе підряд.- Правило: Якщо значення можна вирахувати з інших пропсів або стейту — не створюйте для нього окремий стейт.
- Приклад: Є
firstNameіlastName. Не робіть стейтfullName. Просто напишітьconst fullName = firstName + ' ' + lastNameпрямо в тілі функції.
-
"Синхронізація, а не події" Не думайте про
useEffectяк про "щось, що стається після кліку". Думайте про це як про синхронізацію.- "Я хочу, щоб заголовок сторінки завжди був синхронізований з кількістю лайків".
-
Обережніше з об'єктами
useState({ name: 'David', age: 30 }). Якщо ви хочете змінити тільки вік, не забудьте скопіювати старі дані:setUser({ ...user, age: 31 }). Якщо ви просто напишетеsetUser({ age: 31 }), ім'я зникне!
6. 🧩 Підсумок
Отже, що ми сьогодні поклали у свій ментальний рюкзак:
useState— це пам'ять компонента. Змінили стейт ➡️ React оновив картинку.useEffect— це міст до зовнішнього світу (API, таймери, DOM).- Масив залежностей — це гальма для
useEffect. Забули про них — отримали хаос.
Тепер ви вмієте створювати компоненти, які реагують на користувача і живуть своїм життям.
👀 Тизер: Але що, якщо нам треба передати дані через 10 компонентів вниз? Передавати пропси вручну — це пекло (prop drilling). На наступному занятті ми розглянемо Context API — телепорт для ваших даних.
А поки що — це був CS50. Практикуйтесь!
(Код закривається, світло гасне)