Kurs Svelte i SvelteKit · Moduł 3: Reaktywność i runy

$state - Stan reaktywny

2 min czytania ·
W tej lekcji5

$state to najpotężniejsza runa - tworzy reaktywny stan, który automatycznie aktualizuje UI przy zmianach.

Podstawowe użycie

<script>
  // Liczba
  let gold = $state(0);

  // String
  let vikingName = $state('Ragnar');

  // Boolean
  let isReady = $state(false);

  // Array
  let warriors = $state(['Bjorn', 'Ivar']);

  // Object
  let ship = $state({
    name: 'Drakkar',
    crew: 50,
    cargo: []
  });
</script>

Aktualizacja stanu

<script>
  let count = $state(0);
  let items = $state(['a', 'b']);
  let user = $state({ name: 'Viking', level: 1 });

  function examples() {
    // Liczby - jak zwykłe przypisanie
    count += 1;
    count = count * 2;

    // Tablice - mutacje działają!
    items.push('c');        // Działa w Svelte 5!
    items[0] = 'x';         // Działa!
    items = [...items, 'd']; // Też działa

    // Obiekty - mutacje działają!
    user.level += 1;        // Działa!
    user.name = 'Jarl';     // Działa!
  }
</script>

Głęboka reaktywność

$state tworzy głęboko reaktywny obiekt:

<script>
  let kingdom = $state({
    name: 'Nordheim',
    villages: [
      { name: 'Svenheim', population: 500 },
      { name: 'Bjornvik', population: 300 }
    ]
  });

  function growVillage() {
    // Zagnieżdżona mutacja - działa automatycznie!
    kingdom.villages[0].population += 100;
  }
</script>

<p>{kingdom.villages[0].name}: {kingdom.villages[0].population}</p>
<button onclick={growVillage}>Rozwijaj wioskę</button>

$state z klasami

<script>
  class Warrior {
    name = $state('');
    health = $state(100);
    weapon = $state('Sword');

    constructor(name) {
      this.name = name;
    }

    takeDamage(amount) {
      this.health -= amount;
    }
  }

  let hero = new Warrior('Ragnar');
</script>

<p>{hero.name}: {hero.health} HP</p>
<button onclick={() => hero.takeDamage(10)}>
  Zadaj obrażenia
</button>

$state.raw - Bez głębokiej reaktywności

Czasem nie potrzebujesz głębokiej reaktywności:

<script>
  // Tylko referencja jest reaktywna, nie zawartość
  let battleLog = $state.raw([
    { year: 793, place: 'Lindisfarne' },
    { year: 845, place: 'Paryż' }
  ]);

  // battleLog.push(...) NIE odświeży widoku - przypisz nową tablicę
  function addBattle() {
    battleLog = [...battleLog, { year: 866, place: 'York' }];
  }
</script>

<p>Bitew w kronice: {battleLog.length}</p>
<button onclick={addBattle}>Dopisz bitwę</button>

Użyj $state.raw dla:

  • Dużych zbiorów danych
  • Danych tylko do odczytu
  • Optymalizacji wydajności
Kod do tej lekcji: App.svelte
<script>
  // $state - deep reactivity

  let warrior = $state({
    name: 'Ragnar',
    stats: {
      health: 100,
      strength: 95,
      energy: 80
    },
    inventory: ['Axe', 'Shield']
  });

  function takeDamage() {
    // Nested mutations work!
    warrior.stats.health -= 10;
  }

  function heal() {
    warrior.stats.health = Math.min(100, warrior.stats.health + 20);
  }

  function addItem(item) {
    warrior.inventory.push(item);
  }
</script>

<main class="warrior-panel">
  <h1>{warrior.name}</h1>

  <section class="stats">
    <h2>Stats</h2>
    <div class="stat">
      <span class="stat-label">Health</span>
      <div class="bar">
        <div class="fill health" style="width: {warrior.stats.health}%"></div>
      </div>
      <span>{warrior.stats.health}/100</span>
    </div>

    <div class="stat">
      <span class="stat-label">Strength</span>
      <div class="bar">
        <div class="fill strength" style="width: {warrior.stats.strength}%"></div>
      </div>
      <span>{warrior.stats.strength}/100</span>
    </div>

    <div class="actions">
      <button onclick={takeDamage} class="damage">Take damage</button>
      <button onclick={heal} class="heal">Heal</button>
    </div>
  </section>

  <section class="inventory">
    <h2>Inventory ({warrior.inventory.length})</h2>
    <ul>
      {#each warrior.inventory as item}
        <li>{item}</li>
      {/each}
    </ul>
    <button onclick={() => addItem('Sword')}>+ Add Sword</button>
  </section>
</main>

<style>
  .warrior-panel {
    font-family: 'Georgia', serif;
    max-width: 500px;
    margin: 0 auto;
    padding: 2rem;
    background: #1a0f0a;
    color: #e8d5b7;
    min-height: 100vh;
  }

  h1 {
    color: #d4af37;
    text-align: center;
  }

  h2 {
    color: #d4af37;
    font-size: 1rem;
    margin-bottom: 1rem;
  }

  section {
    background: rgba(212, 175, 55, 0.1);
    border: 1px solid #d4af37;
    border-radius: 8px;
    padding: 1rem;
    margin-bottom: 1rem;
  }

  .stat {
    margin-bottom: 0.75rem;
  }

  .stat .stat-label {
    display: block;
    margin-bottom: 0.25rem;
  }

  .bar {
    height: 20px;
    background: rgba(0,0,0,0.5);
    border-radius: 4px;
    overflow: hidden;
  }

  .fill {
    height: 100%;
    transition: width 0.3s;
  }

  .fill.health { background: linear-gradient(90deg, #8b0000, #ff4444); }
  .fill.strength { background: linear-gradient(90deg, #d4af37, #ffd700); }

  .actions {
    display: flex;
    gap: 0.5rem;
    margin-top: 1rem;
  }

  button {
    flex: 1;
    padding: 0.5rem;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    color: white;
  }

  .damage { background: #8b0000; }
  .heal { background: #2d5016; }

  ul {
    list-style: none;
    padding: 0;
  }

  li {
    padding: 0.5rem;
    background: rgba(0,0,0,0.3);
    margin: 0.25rem 0;
    border-radius: 4px;
  }
</style>

Hasła z dokumentacji w tej lekcji

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Jak stworzyć reaktywny stan w Svelte 5?

  2. 2. Co jest prawdą o $state w Svelte 5?

Zadania praktyczne w grze

  • Edytor kodu

    Uzupełnij kod tworząc reaktywną tablicę wojowników

Przydatne artykuły