restate / v1.0.0
何時儲存由你決定
把 store 存到 IndexedDB 或 MMKV,啟動時載入一次,明確或自動儲存,並顯示未儲存的修改。
宣告持久化 store
把 restate 換成 persistedRestate 並給它一個 key。元件繼續使用同一個 Hook 與 set。
建立後會自動還原已儲存的值。編輯只會改變記憶體;在你儲存或開啟 autoSave 之前,不會寫入任何東西。
import { persistedRestate } from '@kkwaenggwari/restate';
export const useDraft = persistedRestate('', {
key: 'article-draft',
validate: (value) => typeof value === 'string',
});啟動時只等待一次
在用戶端進入點匯入應用程式及其 store,然後在渲染前等待 restateManager.ready。之後元件就能直接讀取已儲存的值。
儲存空間無法使用(例如伺服器端渲染或瀏覽器封鎖了儲存空間)不算失敗:這些 store 會在記憶體中運作。只有在紀錄無法讀取、遷移或驗證時,ready 才會被拒絕。
import { createRoot } from 'react-dom/client';
import { restateManager } from '@kkwaenggwari/restate';
import App from './App';
async function start() {
const root = createRoot(document.getElementById('root'));
try {
await restateManager.ready;
root.render(<App />);
} catch (error) {
root.render(<p>Could not load saved data.</p>);
console.error(error);
}
}
start();儲存、復原與重設
save 寫入目前的值,restore 回到已儲存的值,resetAll 會同時清除記憶體與儲存空間。它們都回傳 Promise。
await useDraft.save(); // write the current value
await useDraft.restore(); // return to the saved value
await useDraft.resetAndClearStorage(); // initial value in memory, record removed顯示未儲存的修改與進度
持久化狀態改變時 useStatus 會重新渲染。當記憶體與上次載入、儲存或還原的值不同時,dirty 為 true。operation 表示正在執行的儲存操作。
傳入 selector,只在某個欄位改變時重新渲染。
function SaveButton() {
const { dirty, operation, error } = useDraft.useStatus();
const saving = operation === 'save';
return (
<>
<button disabled={!dirty || saving} onClick={() => useDraft.save().catch(() => {})}>
{saving ? 'Saving…' : 'Save'}
</button>
{error != null && <p>Could not save your draft.</p>}
</>
);
}動手試試
輸入內容,調高儲存空間延遲後儲存。重新播放範例,就能看到已儲存的草稿被還原。
正在準備範例…
自動儲存
對於一律要儲存的設定與草稿,只要開啟一次。autoSave: true 會在每次變化後儲存;{ delayMs } 會等到變化停頓。
連續的變化會合併為一次寫入,改回已儲存的值則不會寫入。失敗會顯示在 status().error 中。元件擁有的 scope 會在卸載時寫入待處理的修改。
export const useTheme = persistedRestate('light', { key: 'theme', autoSave: true });
export const useDraft = persistedRestate('', {
key: 'article-draft',
autoSave: { delayMs: 500 },
});頁面關閉之前
在 React 之外也能讀取同樣的狀態。有未儲存的修改時可以在離開前提醒,或在頁面隱藏時儲存全部內容。
window.addEventListener('beforeunload', (event) => {
if (useDraft.status().dirty) event.preventDefault();
});
window.addEventListener('pagehide', () => {
restateManager.saveAll();
});處理儲存錯誤
儲存錯誤會讓該操作的 Promise 被拒絕。每個 restate 錯誤都有 code,持久化 store 的錯誤還會帶有紀錄的 key。
try {
await useDraft.save();
} catch (error) {
if (error.code === 'STORAGE_UNAVAILABLE') showNotice('Saving is off in this browser.');
else if (error.code === 'STORAGE_VALUE_TOO_LARGE') showNotice('This draft is too large.');
else showNotice(`Could not save "${error.key ?? 'draft'}".`);
}