Списки и ключи

Atomico — библиотека для создания веб-компонентов на JavaScript с реактивным подходом. Работа со списками и управление ключами является важной частью оптимизации рендеринга и поддержания согласованного состояния компонентов.


Рендеринг списков

Списки в Atomico создаются с помощью функции map, которая используется внутри JSX-подобного синтаксиса. Основная цель — корректно отображать массив данных в DOM без потери реактивности.

Пример базового рендеринга списка:

import { c, useState } from "atomico";

function TodoList() {
    const [todos, setTodos] = useState([
        { id: 1, text: "Купить хлеб" },
        { id: 2, text: "Помыть машину" },
        { id: 3, text: "Позвонить другу" }
    ]);

    return (
        <host>
            <ul>
                {todos.map(todo => (
                    <li>{todo.text}</li>
                ))}
            </ul>
        </host>
    );
}

export const todoList = c(TodoList);

В этом примере каждый элемент массива todos отображается как <li>. Однако при обновлении массива порядок элементов может изменяться, и без явного указания ключей Atomico будет вынужден пересоздавать все элементы, что снижает производительность и может привести к потере состояния вложенных компонентов.


Значение ключей

Ключ (key) — это уникальный идентификатор элемента списка, который позволяет Atomico отслеживать его между рендерами. Ключи обеспечивают:

  • Стабильность состояния: внутреннее состояние компонента сохраняется при изменении порядка элементов.
  • Оптимизацию перерисовки: DOM обновляется минимально, изменяются только добавленные, удаленные или перемещенные элементы.
  • Предотвращение багов: например, если в списке есть <input> с пользовательским вводом, без ключа значение может сбрасываться при изменении массива.

Правильное использование ключей:

<ul>
    {todos.map(todo => (
        <li key={todo.id}>{todo.text}</li>
    ))}
</ul>

Важно: ключи должны быть уникальными среди элементов текущего списка, стабильными между рендерами и не использовать случайные значения вроде индекса массива, если элементы могут переставляться.


Использование индекса как ключа

Использование индекса допустимо только в случаях, когда порядок элементов никогда не меняется и элементы не добавляются/удаляются динамически:

<ul>
    {todos.map((todo, index) => (
        <li key={index}>{todo.text}</li>
    ))}
</ul>

Однако при динамическом обновлении списка это может привести к неожиданному поведению, например:

  • Перезапись состояния вложенных компонентов.
  • Некорректная анимация при изменении порядка элементов.
  • Потеря введенных пользователем данных в формах.

Обновление элементов списка

Atomico использует реактивные хуки, такие как useState, для управления массивами данных. При добавлении, удалении или изменении элементов необходимо создавать новый массив, чтобы реактивность сработала корректно.

Пример добавления элемента:

function addTodo() {
    setTodos([...todos, { id: Date.now(), text: "Новый пункт" }]);
}

Пример удаления элемента:

function removeTodo(id) {
    setTodos(todos.filter(todo => todo.id !== id));
}

Пример обновления текста:

function updateTodo(id, newText) {
    setTodos(todos.map(todo => todo.id === id ? { ...todo, text: newText } : todo));
}

В каждом случае используется новый массив, что гарантирует корректное срабатывание реактивного рендера Atomico.


Вложенные списки и ключи

При работе с вложенными списками ключи необходимо задавать для каждого уровня:

const categories = [
    { id: 1, name: "Фрукты", items: ["Яблоко", "Банан"] },
    { id: 2, name: "Овощи", items: ["Морковь", "Помидор"] }
];

<ul>
    {categories.map(cat => (
        <li key={cat.id}>
            {cat.name}
            <ul>
                {cat.items.map((item, idx) => (
                    <li key={idx}>{item}</li>
                ))}
            </ul>
        </li>
    ))}
</ul>

Для вложенного списка индексы допустимы только если порядок элементов фиксирован. Для динамических вложенных данных лучше использовать уникальные идентификаторы.


Итоговые рекомендации

  • Всегда задавать ключи при рендеринге списков.
  • Ключ должен быть уникальным и стабильным.
  • Не использовать индекс массива как ключ при динамическом изменении элементов.
  • Создавать новый массив при изменении данных, чтобы Atomico корректно отслеживал изменения.
  • Для вложенных списков задавать ключи на каждом уровне.

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