quasiQuest: useContext

Porozmawiaj z LLM na temat hooka useContext i przetestuj przykłady. Zacznij od prompta:


Stwórz przykładową aplikację w React używającą Vite. Chodzi o pokazanie, kiedy warto użyć hooka useContext zamiast przekazywania propsów. 

Wymagania:
1. Pokaż co najmniej jeden przypadek użycia useContext (np. dane użytkownika, motyw, język aplikacji, ustawienia globalne).
2. Pokaż prosty przykład "przed" użyciem useContext, gdzie dane są przekazywane przez propsy w kilku poziomach komponentów.
3. Pokaż "po" użyciu useContext – te same dane są dostępne w głębokich komponentach bez props drilling.
4. Kod powinien być czytelny, działający w projekcie Vite + React.
5. Dodaj komentarze wyjaśniające, dlaczego w danym miejscu użycie useContext jest przydatne.

Quest: Częsty błąd z useState

Licznik z błędem

Przeanalizuj poniższy przykład, a następnie go uruchom. Czy działa zgodnie z oczekiwaniem?

import React, { useState } from "react";

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

  const handleIncrement = () => {
    // Błąd: oba wywołania używają starej wartości count
    setCount(count + 1);
    setCount(count + 1);
  };

  return (
    <div style={{ padding: "20px" }}>
      <h1>Błędne zwiększanie licznika</h1>
      <p>Licznik: {count}</p>
      <button onClick={handleIncrement}>Zwiększ dwa razy</button>
    </div>
  );
}

export default App;

React grupuje wiele zmian stanu lub propsów w jednym renderze, zamiast renderować komponent osobno po każdej zmianie. Jeśli w jednej funkcji wywołasz setState kilka razy, React nie wykona tyle renderów, ile wywołań tylko zrobi tylko jeden render na koniec.
Poprawna wersja:

const handleIncrement = () => {
    // Poprawnie: każda aktualizacja korzysta z aktualnej wartości
    setCount(prev => prev + 1);
    setCount(prev => prev + 1);
  };

Przykład z tablicą

import React, { useState } from "react";

function App() {
  const [items, setItems] = useState([]);

  const addItems = () => {
    // Błąd: oba wywołania używają starej wartości items
    setItems([...items, "A"]);
    setItems([...items, "B"]);
  };

  return (
    <div style={{ padding: "20px" }}>
      <h1>Błędne dodawanie elementów</h1>
      <button onClick={addItems}>Dodaj A i B</button>
      <ul>
        {items.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
}

export default App;

Napisz poprawną wersję addItems wykorzystującą prev

Zasada: Używaj setState(prev => …) gdy:

  • nowa wartość zależy od poprzedniej
  • aktualizujesz licznik
  • aktualizujesz tablicę
  • aktualizujesz obiekt

Quest: Bootstrap z CSSem w parze

Przykład poniżej pokazuje, że wygląd Bootstrapa 5 można zmieniać (bez SCSS), korzystając z kaskady CSS oraz zmiennych CSS wbudowanych w framework.

Nadpisanie zmiennych globalnych (:root) zmienia cały motyw aplikacji, natomiast nadpisanie zmiennych w kontenerze działa tylko lokalnie, w obrębie danej sekcji. Nadpisywanie zmiennych komponentu (np. przycisków) jest bezpieczniejsze i bardziej przewidywalne niż klasyczne nadpisywanie właściwości typu background-color.
Spróbuj zmieniać wartości zmiennych i poprzenosić między style sekcjami, żeby zobaczyć, jak kaskada i dziedziczenie CSS wpływają na wygląd.

<!doctype html>
<html lang="pl">
<head>
  <meta charset="utf-8">
  <title>Bootstrap 5 – nadpisywanie stylów</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- Bootstrap -->
  <link
    href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
    rel="stylesheet"
  >

  <!-- Custom overrides -->
  <style>
    /* ===============================
       1. Globalne nadpisanie zmiennych
       =============================== */
    :root {
      --bs-primary: #6f42c1;
      --bs-body-bg: #f5f3ff;
      --bs-body-color: #2d2a32;
      --bs-border-radius: 1rem;
    }

    /* ===============================
       2. Nadpisanie komponentu (btn)
       =============================== */
    .btn-primary {
      --bs-btn-bg: #6f42c1;
      --bs-btn-border-color: #6f42c1;
      --bs-btn-hover-bg: #59339d;
      --bs-btn-hover-border-color: #59339d;
    }

    /* ===============================
       3. Klasyczne nadpisanie CSS
       =============================== */
    .card {
      border-width: 2px;
    }

    /* ===============================
       4. Lokalny kontekst (sekcja dark)
       =============================== */
    .dark-section {
      --bs-body-bg: #1e1b29;
      --bs-body-color: #f1f1f1;
      --bs-card-bg: #2a2638;
      --bs-primary: #f72585;
    }

    /* ===============================
       5. Custom component
       =============================== */
    .custom-alert {
      border-left: 6px solid var(--bs-primary);
      background-color: #fff;
    }
  </style>
</head>

<body class="py-4">

<div class="container">
  <h1 class="mb-4">Bootstrap 5 – nadpisywanie stylów (bez SCSS)</h1>

  <!-- ===============================
       PRZYCISKI
       =============================== -->
  <section class="mb-5">
    <h2>Przyciski (zmienne komponentu)</h2>
    <button class="btn btn-primary me-2">Primary</button>
    <button class="btn btn-outline-primary">Outline</button>
  </section>

  <!-- ===============================
       KARTY
       =============================== -->
  <section class="mb-5">
    <h2>Karty (globalne zmienne + CSS)</h2>
    <div class="row g-3">
      <div class="col-md-4">
        <div class="card p-3">
          <h5 class="card-title">Card domyślna</h5>
          <p class="card-text">Zmodyfikowany radius i border.</p>
        </div>
      </div>
      <div class="col-md-4">
        <div class="card p-3 border-primary">
          <h5 class="card-title">Card z utility</h5>
          <p class="card-text">Kolor przez klasy pomocnicze.</p>
        </div>
      </div>
    </div>
  </section>

  <!-- ===============================
       ALERTY
       =============================== -->
  <section class="mb-5">
    <h2>Alerty (różne podejścia)</h2>

    <div class="alert alert-primary">
      Standardowy alert Bootstrap
    </div>

    <div class="alert custom-alert">
      Alert z własnym komponentem
    </div>
  </section>

  <!-- ===============================
       FORMULARZ
       =============================== -->
  <section class="mb-5">
    <h2>Formularz</h2>
    <form class="card p-4">
      <div class="mb-3">
        <label class="form-label">Email</label>
        <input type="email" class="form-control">
      </div>
      <div class="mb-3">
        <label class="form-label">Hasło</label>
        <input type="password" class="form-control">
      </div>
      <button class="btn btn-primary">Zaloguj</button>
    </form>
  </section>
</div>

<!-- ===============================
     SEKCJA LOKALNA (dark mode)
     =============================== -->
<section class="dark-section py-5 mt-5">
  <div class="container">
    <h2>Sekcja z lokalnie nadpisanymi zmiennymi</h2>
    <p>
      Ten sam Bootstrap, inne zmienne CSS – bez duplikowania stylów.
    </p>
    <button class="btn btn-primary">Primary w dark mode</button>

    <div class="card p-3 mt-3">
      <h5 class="card-title">Card w dark section</h5>
      <p class="card-text">Zmienne działają kontekstowo.</p>
    </div>
  </div>
</section>

</body>
</html>