restate / v1.0.0

组件拥有的状态

为每个文档、对话框或请求提供专属的 store:随组件一起创建,在组件卸载后释放。

本页内容useRestateScope工厂函数的规则用 props 提供初始值服务端渲染等待文档的已保存数据动手试试React 之外的所有者

useRestateScope

工厂函数声明并返回 store。restate 会在多次渲染之间保留结果,并在组件卸载后销毁 scope。不需要编写关闭函数或清理用的 effect。

两个 Document 元素拥有各自独立的状态。要和子组件共享一个文档,可以通过 props 或你自己的 context 传递返回的 Hook。

JavaScript / JSX
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 可能多次调用工厂函数并丢弃结果,所以请保持它同步且纯净。

  • 在工厂函数中声明 store;在组件的顶层调用返回的 Hook。
  • 初始 props 只读取一次,就像 useState 的初始化函数。
  • 需要为另一个文档提供全新状态时,给组件加上 key。
  • 不传工厂函数时,useRestateScope() 会返回一个稳定的 scope,供你之后在 effect 或事件处理函数中创建 store。

用 props 提供初始值

把 props 作为初始值,用 React 的 key 为另一个文档重新开始。

JavaScript / JSX
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 传入。初始值同时也是服务端快照。在 Next.js App Router 中,这个 Hook 应放在 Client Component 里,Client Component 同样会在服务端渲染。

JavaScript / JSX
'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>;
}

等待文档的已保存数据

scope 中的持久化 store 在组件于客户端提交后才开始恢复,不包含在全局的 restateManager.ready 中。请在加载边界中等待该 scope 自己的 manager.ready。

JavaScript / JSX
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 之外的所有者

当服务或服务端请求拥有状态时,自己创建 scope,用完后调用 destroy()。销毁 scope 绝不会删除已保存的记录。

JavaScript / JSX
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(),
  };
}

搜索文档

搜索所有指南的标题、描述和正文。