@State для внутреннего состояния

В фреймворке Stencil декоратор @State используется для управления внутренним состоянием компонента. Он позволяет создавать реактивные переменные, которые автоматически обновляют представление компонента при изменении их значений. Состояние компонента — это данные, которые могут изменяться в процессе его работы и влиять на отображение содержимого.

Принципы работы с @State

При использовании @State необходимо понимать, что такие переменные инкапсулируются внутри компонента. Изменение значения состояния приводит к повторному рендеру компонента, что делает его реактивным.

Для использования @State достаточно просто пометить переменную в классе компонента:

import { Component, h, State } from '@stencil/core';

@Component({
  tag: 'my-counter',
  styleUrl: 'my-counter.css',
  shadow: true,
})
export class MyCounter {
  @State() count: number = 0;

  increment() {
    this.count += 1;
  }

  render() {
    return (
      <div>
        <p>{this.count}</p>
        <button onCl ick={() => this.increment()}>Increment</button>
      </div>
    );
  }
}

В этом примере переменная count помечена как состояние компонента с помощью @State. При изменении count, компонент будет автоматически повторно рендериться, что позволяет отобразить актуальное значение счётчика.

Повторный рендеринг

Одним из основных принципов работы с состоянием является реактивность: любые изменения в состоянии вызывают повторный рендер компонента. Однако важно понимать, что Stencil применяет стратегию дифференцированного рендеринга, то есть обновляется только та часть DOM, которая была затронута изменением состояния.

Это помогает снизить нагрузку на производительность, так как избегается полная перерисовка компонента. Только те элементы, которые напрямую зависят от изменённого состояния, будут обновлены.

Использование с несколькими состояниями

Компонент может содержать несколько переменных, помеченных декоратором @State. Каждое из этих состояний будет работать независимо и вызывать ререндеринг только тех элементов, которые с ним связаны.

@Component({
  tag: 'my-counter',
  styleUrl: 'my-counter.css',
  shadow: true,
})
export class MyCounter {
  @State() count: number = 0;
  @State() isEven: boolean = true;

  increment() {
    this.count += 1;
    this.isEven = this.count % 2 === 0;
  }

  render() {
    return (
      <div>
        <p>{this.count}</p>
        <p>{this.isEven ? 'Even' : 'Odd'}</p>
        <button onCl ick={() => this.increment()}>Increment</button>
      </div>
    );
  }
}

В этом примере есть две переменные состояния: count и isEven. При каждом изменении count также обновляется isEven, поскольку оно зависит от чётности счётчика. Оба состояния управляют разными частями DOM, что позволяет эффективно обновлять только те элементы, которые нуждаются в изменении.

Связь с props

Стоит отметить, что переменные, помеченные @State, существуют только внутри компонента и не передаются наружу как свойства (props). Это отличает состояние от свойств компонента. Если необходимо передавать данные между компонентами, следует использовать @Prop.

Таким образом, @State помогает изолировать логику компонента и сохраняет инкапсуляцию, не позволяя напрямую изменять его состояние извне.

Модификация состояния

Для изменения состояния внутри компонента необходимо использовать стандартные методы, такие как присваивание нового значения. Однако стоит помнить, что не рекомендуется изменять состояние напрямую через мутации объектов или массивов, так как это может не вызвать перерендер компонента. Вместо этого нужно использовать новые ссылки на данные.

@State() items: string[] = [];

addItem(item: string) {
  this.items = [...this.items, item];  // Создание нового массива
}

Модификация массива или объекта с помощью мутации (например, this.items.push(item)) не вызовет перерендер, так как ссылка на данные не изменяется. Вместо этого следует создать новый объект или массив, чтобы Stencil обнаружил изменение и инициировал обновление.

Логика рендеринга

Stencil использует стратегию декларативного рендеринга, где компонент описывает своё представление в функции render(), которая зависит от состояния. Каждый раз, когда состояние изменяется, Stencil вызывает эту функцию, чтобы пересоздать виртуальное представление компонента и синхронизировать его с DOM.

render() {
  return (
    <div>
      {this.count > 0 ? (
        <p>Счётчик больше нуля: {this.count}</p>
      ) : (
        <p>Счётчик равен нулю</p>
      )}
    </div>
  );
}

В данном примере условный рендеринг основывается на значении состояния count. Если счётчик больше нуля, отображается одно сообщение, иначе — другое. Stencil автоматически обработает это изменение, при необходимости обновив только часть DOM, которая зависит от состояния.

Состояние в Shadow DOM

Одним из ключевых преимуществ Stencil является использование Shadow DOM для инкапсуляции компонентов. Состояние компонента изолировано от внешнего мира, что исключает возможность прямого вмешательства в его работу.

Когда состояние компонента изменяется, обновляется только тот DOM, который находится внутри Shadow DOM. Это делает компоненты Stencil особенно мощными для разработки современных веб-приложений, так как они не зависят от внешнего контекста и не влияют на другие компоненты.

Производительность

Stencil применяет методы оптимизации, такие как дебаунсинг и виртуализация, чтобы минимизировать излишние рендеры и обновления. Каждый компонент оценивается по различным критериям, таким как необходимость в обновлении DOM. В некоторых случаях Stencil может объединять несколько изменений состояния в одно обновление, чтобы минимизировать количество операций рендеринга и повысить производительность.

Резюме

Использование @State в Stencil позволяет эффективно управлять внутренним состоянием компонента. Это делает компонент реактивным и автоматизирует процесс обновления представления при изменении данных. Важно помнить о правильном использовании ссылок на данные, чтобы избежать лишних рендеров. Состояние изолировано внутри компонента и не передаётся наружу, что способствует лучшей инкапсуляции и облегчает создание сложных компонентов.