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

搜尋文件

搜尋所有指南的標題、說明與內文。