Перейти к содержанию
Mercury
Русский
Дизайн книга
Меню
Русский

Reflect.4. Сохраняем строки по ключам

У строки есть идентичность и позиция. list получает коллекцию из хранилища, а Hyperscript сохраняет связь между ключом строки и её локальным состоянием.

Reflect 4.1.1 · Интерактивное упражнение

Открыть упражнение на всю страницу

Разделите ключ и позицию

Флажок в useState принадлежит смонтированной строке. После разворота коллекции отметка должна остаться у своего элемента, а не у первой позиции на экране. Передайте в list устойчивый getKey, а номер для отображения вычисляйте отдельно.

import {createElement as h} from "@fiberfx/hyperscript";
import {useState} from "@fiberfx/hyperscript/hooks";
import {event, store} from "@fiberfx/hypergraph";
import {Checkbox} from "@fiberfx/ui";
import {list} from "@fiberfx/reflect";

type Item = {id: string; title: string};
const removed = event<string>();
const reversed = event<void>();
const $items = store<Item[]>([
  {id: "a", title: "Alpha"}, {id: "b", title: "Beta"},
]).on(removed, (items, id) => items.filter(item => item.id !== id))
  .on(reversed, items => [...items].reverse());
const Row = (p: {item: Item; position: number; remove: () => void}) => {
  const [checked, setChecked] = useState(false);
  return h("li", null,
    h(Checkbox, {label: `${p.position}. ${p.item.title}`, checked, onChange: setChecked}),
    h("button", {onClick: p.remove}, "Remove"));
};
export const Rows = list({
  source: $items, view: Row, getKey: item => item.id,
  mapItem: {
    item: item => item,
    position: (_, index) => index + 1,
    remove: item => () => removed(item.id),
  },
});

Выведите h("ul", null, h(Rows, {})) внутри Provider приложения. Функция remove, полученная из отображения элемента, привязана к области, поэтому событие меняет нужную коллекцию. Флажок в этом примере хранится в представлении; выбор, который должен сохраняться как данные приложения, обычно относится к модели.

mapItem заменяет свойства элемента. Он не добавляет {id, title} к результату отображения выше. Без mapItem объекты коллекции передаются как свойства строк. Общий bind задаёт значения по умолчанию; свойства строки имеют над ними приоритет.

Переместите отмеченную строку

Откройте эксперимент со списком. Отметьте Alpha и нажмите Развернуть порядок. Верните порядок, удалите Beta и после каждого действия проверьте отметку и номер Alpha.

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

Проследите сохранение строки

Флажок Alpha должен остаться отмеченным после перестановки, а номер позиции — измениться. Удаление Beta должно затронуть только Beta. Удаление Alpha завершит время жизни её строки; последующее восстановление создаст строку с новым локальным состоянием.

В регрессионном тесте сохраните DOM-узел Alpha до перестановки и сравните его после неё, отдельно от проверки состояния. Правильный текст не обнаружит лишнее размонтирование. Новый объект элемента с тем же ключом может обновить свойства без замены строки.

Отклоните неверную коллекцию

Сам пакет отклоняет повторяющиеся нормализованные ключи, числовые ключи NaN и Infinity, источник не-хранилище и состояние не-массив. 1 и "1" дают одинаковый ключ; 1 и "01" — разные. Эти отказы проверяйте отдельно от защитной проверки приложения в эксперименте.

Без mapItem нельзя передавать примитивы, null и массивы как элементы. Отображение должно покрыть обязательные свойства, которые не покрыты bind или mapProps; пересекаться по ключам с mapProps оно не может. Для представления с дискриминированным объединением передавайте готовые объекты либо обёртку item: независимые отображения полей не доказывают их согласованность.

Осознанно восстановите список

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

Повторите опыт с отмеченной Gamma вместо Alpha. Затем удалите отмеченную строку и убедитесь, что её отметка не перешла к другому элементу.

Соберите редактируемую очередь

Дайте каждой записи очереди устойчивый предметный идентификатор. Видимый номер вычисляйте через mapItem, но не используйте его как идентичность, если возможны вставки и перестановки. Без getKey список намеренно использует позиции; это подходит только действительно позиционным коллекциям.

Добавьте пустой экран через variant, а состояния загрузки и ошибки оставьте вне отображения строки. Следующая глава задаёт эти альтернативные экраны явно.

Назад · Курс · Далее