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

Reflect.6. Заменяем компоненты

Заменяемую часть можно передать обычным свойством компонента. Конструкторы хранятся в постоянном реестре, в модели — выбранное имя, а проекция возвращает нужный компонент без обёртки.

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

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

Передайте конструктор как значение

Функции выполняют в Reflect разные роли. Функция в bind или mapItem оборачивается как обработчик, привязанный к области. Функция из mapProps остаётся ровно тем значением, которое вернула проекция. Конструктору функции или класса и callback ref, для которого важна идентичность, нужен второй путь.

import {createElement as h, type ComponentType} from "@fiberfx/hyperscript";
import {useState} from "@fiberfx/hyperscript/hooks";
import {store} from "@fiberfx/hypergraph";
import {reflect} from "@fiberfx/reflect";

const Compact = () => {
  const [count, setCount] = useState(0);
  return h("button", {onClick: () => setCount(n => n + 1)}, `Compact: ${count}`);
};
const Detailed = () => {
  const [count, setCount] = useState(0);
  return h("section", null, h("h3", null, "Details"),
    h("button", {onClick: () => setCount(n => n + 1)}, `Detailed: ${count}`));
};
const Unknown = () => h("p", null, "Unknown component");
const registry = {compact: Compact, detailed: Detailed};
const $kind = store<string>("compact");
function selectView(kind: string): ComponentType<{}> {
  return Object.hasOwn(registry, kind)
    ? registry[kind as keyof typeof registry] : Unknown;
}
export const Host = reflect({
  view: ({View}: {View: ComponentType<{}>}) => h(View, {}),
  bind: {},
  mapProps: {View: {source: $kind, fn: selectView}},
});

Объявляйте реестр и конструкторы вне рендера. Идентификатор в модели можно сериализовать; сами конструкторы остаются кодом приложения. Не возвращайте () => h(Compact) из проекции: при каждом вычислении получится новый конструктор, что может привести к перемонтированию потомка.

Для выбора по строке оберните данные через mapItem: {item: item => item} и вычислите RowView с source: {} по props.item.kind. Пустой источник означает зависимость от входных свойств. Это не специальный дескриптор слота.

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

Откройте эксперимент с компонентами. Дважды увеличьте локальный счётчик краткого представления, затем выберите подробное. Увеличьте его один раз и вернитесь к краткому. После этого выберите неизвестное имя.

Перед каждой сменой предскажите, сохранится ли число. Это локальное состояние компонента, а не хранилище общей модели.

Отличите обновление от замены

При переходе от краткого представления к подробному счётчик должен начаться с нуля. При возврате к краткому — тоже: прежний экземпляр был удалён, а не отложен для повторного использования. Неизвестное имя должно открыть явный запасной вариант.

В списке сохранение ключа строки — только часть условия идентичности. Тот же дочерний конструктор сохраняет локальное состояние при обновлении и перестановке строки. Другой конструктор намеренно заменяет потомка даже при прежнем ключе строки. Если данные должны пережить смену вида, назначьте им владельца с большим временем жизни в модели.

Выберите правильный путь для функции

Не помещайте конструктор, чья идентичность важна, в bind или mapItem: эти пути оборачивают функции. Не считайте событие или эффект из проекции привязанным к области. Проекция сохраняет объект и методы, но для вызова всё ещё нужны bind, собственный useUnit потомка или явная привязка к области.

Проверяйте имя через собственное свойство реестра. constructor и __proto__ не становятся допустимыми вариантами из-за наличия прототипа у объекта. Эксперимент выбирает запасной вид; приложение может вместо этого явно отклонить данные. Унаследованный член выбирать нельзя.

Начните с одного конструктора

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

В отдельном тесте идентичности храните callback ref в постоянном реестре, возвращайте через mapProps и проверьте, что перестановка строки не вызывает отключение ref. При смене самого конструктора старый ref должен отключиться, а новый потомок — подключиться.

Добавьте краткий и подробный вид строки

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

Спроецированный потомок читает контекст в месте рендера. Его собственный useUnit может использовать вложенный Provider, но уже связанный внешний обработчик сохраняет исходную область. Для такой композиции API именованных слотов или пресетов не нужен.

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