restate / v1.0.0
Состояние, принадлежащее компоненту
Дайте каждому документу, диалогу или запросу собственные хранилища, которые создаются вместе с компонентом и освобождаются после его размонтирования.
На этой странице
useRestateScopeПравила фабрикиНачальные значения из propsРендеринг на сервереОжидание сохранённых данных документаПопробуйтеВладельцы вне ReactuseRestateScope
Фабрика объявляет хранилища и возвращает их. restate хранит результат между рендерами и уничтожает область после размонтирования компонента. Никаких функций закрытия или эффектов очистки писать не нужно.
Два элемента Document имеют независимые состояния. Чтобы передать документ дочерним компонентам, передайте возвращённые хуки через props или свой контекст.
import { restate, useRestateScope } from '@kkwaenggwari/restate';
function Document() {
const { useDraft, useSelection } = useRestateScope((scope) => ({
useDraft: restate('', { scope }),
useSelection: restate(0, { scope }),
}));
const text = useDraft();
const selection = useSelection();
return (
<section>
<input value={text} onChange={(event) => useDraft.set(event.target.value)} />
<button onClick={() => useSelection.set((n) => n + 1)}>Selection: {selection}</button>
</section>
);
}Правила фабрики
React может вызывать фабрику несколько раз и отбрасывать результаты, поэтому она должна быть синхронной и чистой.
- Объявляйте хранилища в фабрике; вызывайте возвращённые хуки на верхнем уровне компонента.
- Начальные props читаются один раз, как в инициализаторе useState.
- Задайте компоненту key, когда другому документу нужно новое состояние.
- Без фабрики useRestateScope() возвращает стабильную область для хранилищ, которые вы создадите позже — в эффектах или обработчиках.
Начальные значения из props
Используйте props как начальные значения, а key React — чтобы начать заново с другим документом.
function Editor({ document }) {
// The initial value is captured once, like a useState initializer.
const useTitle = useRestateScope((scope) => restate(document.title, { scope }));
const title = useTitle();
return <input value={title} onChange={(event) => useTitle.set(event.target.value)} />;
}
// A different document gets fresh state.
<Editor key={document.id} document={document} />;Рендеринг на сервере
Загрузите данные в серверном маршруте и передайте их как prop. Начальное значение служит и серверным снимком. В App Router Next.js этот хук размещается в Client Component, который всё равно рендерится на сервере.
'use client';
import { restate, useRestateScope } from '@kkwaenggwari/restate';
export default function Profile({ user }) {
const useProfile = useRestateScope((scope) => restate(user, { scope }));
const profile = useProfile();
return <p>{profile.name}</p>;
}Ожидание сохранённых данных документа
Сохраняемые хранилища в области начинают восстановление после коммита компонента на клиенте и не входят в глобальный restateManager.ready. Дожидайтесь собственного manager.ready области внутри границы загрузки.
import { useEffect, useState } from 'react';
import { persistedRestate, useRestateScope } from '@kkwaenggwari/restate';
function Document({ documentId }) {
const { manager, useDraft } = useRestateScope((scope) => ({
manager: scope.manager,
useDraft: persistedRestate('', { scope, namespace: 'documents', key: documentId }),
}));
const [state, setState] = useState('loading');
useEffect(() => {
let active = true;
manager.ready.then(
() => active && setState('ready'),
() => active && setState('error'),
);
return () => {
active = false;
};
}, [manager]);
if (state === 'error') return <p>Could not load this document.</p>;
if (state === 'loading') return <p>Loading document…</p>;
return <Editor useDraft={useDraft} />;
}Попробуйте
Открывайте и закрывайте документы и смотрите, как их состояние начинается с нуля.
Подготовка примера…
Владельцы вне React
Когда состояние принадлежит сервису или серверному запросу, создайте область сами и вызовите destroy() в конце. Уничтожение области никогда не удаляет сохранённые записи.
import { createRestateScope, persistedRestate } from '@kkwaenggwari/restate';
export function createDocument(documentId) {
const scope = createRestateScope();
const useDraft = persistedRestate('', {
scope,
namespace: 'documents',
key: documentId,
});
return {
useDraft,
manager: scope.manager,
save: () => scope.manager.saveAll(),
destroy: () => scope.destroy(),
};
}