Presentational vs Container

Atomico — современная библиотека для создания веб-компонентов с реактивностью, схожей с React, но полностью основанная на стандартах Web Components. Одним из ключевых подходов в организации компонентов является разделение на Presentational и Container компоненты. Этот подход позволяет строить архитектуру приложения более чистой, понятной и переиспользуемой.


Presentational компоненты

Presentational (или “декоративные”) компоненты отвечают только за отображение данных и пользовательский интерфейс. Они:

  • Не содержат логики работы с состоянием приложения;
  • Получают данные исключительно через props;
  • Не знают о том, откуда эти данные приходят;
  • Могут быть легко переиспользованы в разных частях приложения.

В Atomico Presentational компонент определяется функцией, возвращающей JSX-подобный синтаксис с использованием html:

import { c, html } from "atomico";

function Button({ label, onClick }) {
    return html`
        <button oncl ick=${onClick}>${label}</button>
    `;
}

Button.props = {
    label: String,
    onClick: Function
};

export const button = c(Button);

Ключевой момент: компонент не использует локальное состояние (useState) и не зависит от внешних сервисов. Он полностью управляется данными, которые ему передают родительские компоненты.


Container компоненты

Container (или “умные”) компоненты отвечают за логику приложения, управление состоянием и получение данных. Их функции:

  • Хранение локального состояния через useState или useStore;
  • Работа с побочными эффектами через useEffect;
  • Подготовка данных для Presentational компонентов;
  • Связывание с API, хранилищами данных или глобальными сервисами.

Пример Container компонента в Atomico:

import { c, html, useState, useEffect } from "atomico";
import { button } from "./Button";

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

    function increment() {
        setCount(count + 1);
    }

    return html`
        <div>
            <p>Текущее значение: ${count}</p>
            <${button} label="Увеличить" onCl ick=${increment} />
        </div>
    `;
}

export const counterContainer = c(CounterContainer);

Здесь видно, что CounterContainer управляет состоянием count и передает его в Presentational компонент Button. Такая структура позволяет отделить визуальную часть от логики, облегчая тестирование и поддержку.


Разделение обязанностей

Основная идея подхода:

  • Presentational компоненты: “что показывать”;
  • Container компоненты: “как и откуда получать данные”.

Преимущества:

  1. Переиспользуемость компонентов. Один и тот же Presentational компонент можно использовать с разными источниками данных.
  2. Упрощение тестирования. Легко тестировать отображение данных без необходимости подключать API или глобальное состояние.
  3. Чистота архитектуры. Логика приложения концентрируется в Container компонентах, визуальная часть остается декларативной.

Связь с props и events

Presentational компоненты в Atomico используют:

  • props для передачи данных сверху вниз;
  • events (emit) для уведомления Container о действиях пользователя.

Пример передачи события:

import { c, html } from "atomico";

function Input({ value, onInput }) {
    return html`<input value=${value} onIn put=${(e) => onInput(e.target.value)} />`;
}

Input.props = {
    value: String,
    onInput: Function
};

export const input = c(Input);

Container может использовать его так:

import { c, html, useState } from "atomico";
import { input } from "./Input";

function FormContainer() {
    const [text, setText] = useState("");

    return html`
        <div>
            <${input} value=${text} onIn put=${setText} />
            <p>Вы ввели: ${text}</p>
        </div>
    `;
}

export const formContainer = c(FormContainer);

Эта схема гарантирует, что Presentational компонент остается чистым и независимым, а Container берет на себя всю сложную логику.


Заключение по структуре компонентов

Разделение на Presentational и Container компоненты в Atomico:

  • Повышает модульность;
  • Делает код более читаемым и поддерживаемым;
  • Облегчает масштабирование приложений на Web Components.

Atomico предоставляет все инструменты для строгого соблюдения этого паттерна: реактивность через useState и useEffect, декларативный синтаксис через html, удобную работу с props и событиями. Это позволяет строить приложения с четкой архитектурой и минимальными связями между слоями логики и представления.