React আর React Native-এর জন্য স্টেট · 1.0
স্টেট ভাগ করুন।একটি হুক।ব্যস।
এক লাইনে স্টোর ঘোষণা করে যেকোনো কম্পোনেন্টে ব্যবহার করুন, Provider, action আর reducer ছাড়াই। কম্পোনেন্ট তখনই আবার রেন্ডার হয় যখন তার বাছাই করা মান বদলায়, আর সংরক্ষণ হয় যখন আপনি ঠিক করেন।
npm install @kkwaenggwari/restateলাইভ — সত্যিকারের restate স্টোর
useCart.set(cart => ({ ...cart, items: cart.items + 1, total: cart.total + 12 }))items আর total বদলায়: তিনটি কার্ড রেন্ডার হয়, গিফট কার্ড স্থির থাকে।
- সহজ0সেটআপের জন্য Provider, action আর reducerমান ঘোষণা করে ব্যবহার করুন। আগে থেকে কিছু জুড়তে হয় না।
- দ্রুত11 → 1এগারোটি পরিবর্তন, একটি লেখাautoSave বিরতির জন্য অপেক্ষা করে, তাই এগারোটি দ্রুত পরিবর্তন একটি লেখা হয়ে যায়।
- স্থিতিশীল26টাইপ করা ত্রুটি কোডপ্রতিটি ত্রুটি জানায় কী ভুল হয়েছে আর কোন রেকর্ডের সঙ্গে সম্পর্কিত।
01 — সহজ
একই সুবিধা, কম ধারণা।
একই কাজের চারটি বাস্তবায়ন পাশাপাশি। সংখ্যাগুলো এখানে দেখানো কোড থেকে আসে এবং এই সাইট বিল্ডের সময় মাপা হয়।
ভাগ করা কাউন্টার
দুটি কম্পোনেন্ট একই সংখ্যা পড়ে ও হালনাগাদ করে।
Redux Toolkit হালনাগাদকে slice আর action-এ সাজায়, যা React context দিয়ে পাওয়া স্টোরে পাঠানো হয়। চমৎকার DevTools আর বড় দলের জন্য কঠোর, অনুমানযোগ্য হালনাগাদ প্রবাহ।
Zustand create() দিয়ে স্টোর হুক তৈরি করে এবং setState দিয়ে বদলায়। ছোট্ট আর নমনীয়, persistence ও DevTools-এর middleware সহ।
Jotai atom আর উদ্ভূত atom জোড়ে, চাইলে Provider-এর ভেতরে। স্টেটের অনেক ছোট টুকরোর মধ্যে সূক্ষ্ম নির্ভরতা।
import { configureStore, createSlice } from '@reduxjs/toolkit';
import { Provider, useDispatch, useSelector } from 'react-redux';
const slice = createSlice({
name: 'count',
initialState: 0,
reducers: { increment: (count) => count + 1 },
});
const store = configureStore({
reducer: { count: slice.reducer },
});
function Counter() {
const count = useSelector((state) => state.count);
const dispatch = useDispatch();
return <button onClick={() => dispatch(slice.actions.increment())}>{count}</button>;
}
export default function App() {
return (
<Provider store={store}>
<Counter />
<Counter />
</Provider>
);
}import { create } from 'zustand';
const useCount = create(() => ({ count: 0 }));
const increment = () =>
useCount.setState((s) => ({
count: s.count + 1,
}));
function Counter() {
const count = useCount((s) => s.count);
return <button onClick={increment}>{count}</button>;
}
export default function App() {
return (
<>
<Counter />
<Counter />
</>
);
}import { atom, useAtom } from 'jotai';
const countAtom = atom(0);
function Counter() {
const [count, setCount] = useAtom(countAtom);
return <button onClick={() => setCount((n) => n + 1)}>{count}</button>;
}
export default function App() {
return (
<>
<Counter />
<Counter />
</>
);
}restate একটি ঘোষণাই স্টোর এবং হুক দুটোই।
import { restate } from '@kkwaenggwari/restate';
const useCount = restate(0);
function Counter() {
const count = useCount();
return <button onClick={() => useCount.set((n) => n + 1)}>{count}</button>;
}
export default function App() {
return (
<>
<Counter />
<Counter />
</>
);
}এই কাজে Zustand আর Jotai-ও ছোট এবং Provider ছাড়া চলে। সংরক্ষণ আর প্রতিটি ডকুমেন্টের স্টেট যোগ হলে পার্থক্য বাড়ে।
ফিল্ড বাছাই
শুধু লেখা বদলালে শিরোনাম কম্পোনেন্ট রেন্ডার হওয়া উচিত নয়।
Redux Toolkit হালনাগাদকে slice আর action-এ সাজায়, যা React context দিয়ে পাওয়া স্টোরে পাঠানো হয়। চমৎকার DevTools আর বড় দলের জন্য কঠোর, অনুমানযোগ্য হালনাগাদ প্রবাহ।
Zustand create() দিয়ে স্টোর হুক তৈরি করে এবং setState দিয়ে বদলায়। ছোট্ট আর নমনীয়, persistence ও DevTools-এর middleware সহ।
Jotai atom আর উদ্ভূত atom জোড়ে, চাইলে Provider-এর ভেতরে। স্টেটের অনেক ছোট টুকরোর মধ্যে সূক্ষ্ম নির্ভরতা।
import { configureStore, createSlice } from '@reduxjs/toolkit';
import { Provider, useSelector } from 'react-redux';
const article = createSlice({
name: 'article',
initialState: { title: 'Hello', body: '' },
reducers: {
editBody: (i) => {
i.body += '!';
},
editTitle: (i) => {
i.title += '!';
},
},
});
const store = configureStore({ reducer: article.reducer });
function Title() {
const title = useSelector((i) => i.title);
return <h3>{title}</h3>;
}
export default function App() {
return (
<Provider store={store}>
<Title />
<button onClick={() => store.dispatch(article.actions.editBody())}>body + !</button>
<button onClick={() => store.dispatch(article.actions.editTitle())}>title + !</button>
</Provider>
);
}import { create } from 'zustand';
const useArticle = create(() => ({ title: 'Hello', body: '' }));
function Title() {
const title = useArticle((i) => i.title);
return <h3>{title}</h3>;
}
export default function App() {
return (
<>
<Title />
<button
onClick={() =>
useArticle.setState((i) => ({
body: i.body + '!',
}))
}
>
body + !
</button>
<button
onClick={() =>
useArticle.setState((i) => ({
title: i.title + '!',
}))
}
>
title + !
</button>
</>
);
}import { atom, useAtomValue, useSetAtom } from 'jotai';
const articleAtom = atom({ title: 'Hello', body: '' });
const titleAtom = atom((get) => get(articleAtom).title);
function Title() {
const title = useAtomValue(titleAtom);
return <h3>{title}</h3>;
}
export default function App() {
const setArticle = useSetAtom(articleAtom);
return (
<>
<Title />
<button
onClick={() =>
setArticle((i) => ({
...i,
body: i.body + '!',
}))
}
>
body + !
</button>
<button
onClick={() =>
setArticle((i) => ({
...i,
title: i.title + '!',
}))
}
>
title + !
</button>
</>
);
}restate যে হুক আগে থেকেই ব্যবহার করছেন, সেটিকেই selector দিন।
import { restate } from '@kkwaenggwari/restate';
const useArticle = restate({ title: 'Hello', body: '' });
function Title() {
const title = useArticle((i) => i.title);
return <h3>{title}</h3>;
}
export default function App() {
return (
<>
<Title />
<button
onClick={() =>
useArticle.set((i) => ({
...i,
body: i.body + '!',
}))
}
>
body + !
</button>
<button
onClick={() =>
useArticle.set((i) => ({
...i,
title: i.title + '!',
}))
}
>
title + !
</button>
</>
);
}এখানের সব টুলই বাছতে পারে। restate আলাদা selector হুক বা উদ্ভূত atom-এর বদলে একই হুক কল ব্যবহার করে।
খসড়া সংরক্ষণ
স্টার্টআপে সংরক্ষিত খসড়া ফিরিয়ে আনুন, ক্লিকে সংরক্ষণ করুন, আর সংরক্ষণ হচ্ছে, সংরক্ষণ হয়নি ও ব্যর্থ অবস্থা দেখান।
Redux Toolkit হালনাগাদকে slice আর action-এ সাজায়, যা React context দিয়ে পাওয়া স্টোরে পাঠানো হয়। চমৎকার DevTools আর বড় দলের জন্য কঠোর, অনুমানযোগ্য হালনাগাদ প্রবাহ।
Zustand create() দিয়ে স্টোর হুক তৈরি করে এবং setState দিয়ে বদলায়। ছোট্ট আর নমনীয়, persistence ও DevTools-এর middleware সহ।
Jotai atom আর উদ্ভূত atom জোড়ে, চাইলে Provider-এর ভেতরে। স্টেটের অনেক ছোট টুকরোর মধ্যে সূক্ষ্ম নির্ভরতা।
import { useEffect, useState } from 'react';
import { configureStore, createSlice } from '@reduxjs/toolkit';
import { Provider, useSelector } from 'react-redux';
function openDraftDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open('draft-example', 1);
let blocked = false;
request.onupgradeneeded = () => {
request.result.createObjectStore('drafts');
};
request.onblocked = () => {
blocked = true;
reject(new Error('Close other tabs to upgrade storage'));
};
request.onerror = () => reject(request.error);
request.onsuccess = () => {
const db = request.result;
if (blocked) {
db.close();
return;
}
db.onversionchange = () => db.close();
resolve(db);
};
});
}
async function storedDraft(mode, text) {
const db = await openDraftDB();
try {
return await new Promise((resolve, reject) => {
const tx = db.transaction('drafts', mode);
const store = tx.objectStore('drafts');
const request = mode === 'readonly' ? store.get('draft') : store.put(text, 'draft');
tx.oncomplete = () => resolve(request.result);
tx.onabort = () => reject(tx.error || new Error('Storage aborted'));
});
} finally {
db.close();
}
}
const draft = createSlice({
name: 'draft',
initialState: '',
reducers: { edit: (_, action) => action.payload },
});
const store = configureStore({ reducer: { draft: draft.reducer } });
const hydrated = storedDraft('readonly').then((saved) => {
if (saved === undefined) return;
if (typeof saved !== 'string') throw new Error('Invalid draft');
const value = saved;
store.dispatch(draft.actions.edit(value));
});
// Observe errors immediately, even before the component mounts.
hydrated.catch(() => {});
function DraftEditor() {
const text = useSelector((state) => state.draft);
const [ready, setReady] = useState(false);
const [saving, setSaving] = useState(false);
const [message, setMessage] = useState('Loading');
useEffect(() => {
let active = true;
hydrated.then(
() => {
if (active) {
setReady(true);
setMessage('Ready');
}
},
() => {
if (active) setMessage('Storage error');
},
);
return () => {
active = false;
};
}, []);
async function save() {
setSaving(true);
try {
await storedDraft('readwrite', store.getState().draft);
setMessage('Saved');
} catch {
setMessage('Save failed');
} finally {
setSaving(false);
}
}
return (
<>
<label>
Draft
<textarea
value={text}
disabled={!ready || saving}
onChange={(event) => {
const value = event.target.value;
store.dispatch(draft.actions.edit(value));
setMessage('Edited');
}}
/>
</label>
<output>{text}</output>
<button disabled={!ready || saving} onClick={save}>
Save
</button>
<p>{message}</p>
</>
);
}
export default function App() {
return (
<Provider store={store}>
<DraftEditor />
</Provider>
);
}import { useEffect, useState } from 'react';
import { create } from 'zustand';
function openDraftDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open('draft-example', 1);
let blocked = false;
request.onupgradeneeded = () => {
request.result.createObjectStore('drafts');
};
request.onblocked = () => {
blocked = true;
reject(new Error('Close other tabs to upgrade storage'));
};
request.onerror = () => reject(request.error);
request.onsuccess = () => {
const db = request.result;
if (blocked) {
db.close();
return;
}
db.onversionchange = () => db.close();
resolve(db);
};
});
}
async function storedDraft(mode, text) {
const db = await openDraftDB();
try {
return await new Promise((resolve, reject) => {
const tx = db.transaction('drafts', mode);
const store = tx.objectStore('drafts');
const request = mode === 'readonly' ? store.get('draft') : store.put(text, 'draft');
tx.oncomplete = () => resolve(request.result);
tx.onabort = () => reject(tx.error || new Error('Storage aborted'));
});
} finally {
db.close();
}
}
const useDraft = create(() => ({ text: '' }));
const hydrated = storedDraft('readonly').then((saved) => {
if (saved === undefined) return;
if (typeof saved !== 'string') throw new Error('Invalid draft');
const value = saved;
useDraft.setState({ text: value });
});
// Observe errors immediately, even before the component mounts.
hydrated.catch(() => {});
function DraftEditor() {
const text = useDraft((state) => state.text);
const [ready, setReady] = useState(false);
const [saving, setSaving] = useState(false);
const [message, setMessage] = useState('Loading');
useEffect(() => {
let active = true;
hydrated.then(
() => {
if (active) {
setReady(true);
setMessage('Ready');
}
},
() => {
if (active) setMessage('Storage error');
},
);
return () => {
active = false;
};
}, []);
async function save() {
setSaving(true);
try {
await storedDraft('readwrite', useDraft.getState().text);
setMessage('Saved');
} catch {
setMessage('Save failed');
} finally {
setSaving(false);
}
}
return (
<>
<label>
Draft
<textarea
value={text}
disabled={!ready || saving}
onChange={(event) => {
const value = event.target.value;
useDraft.setState({ text: value });
setMessage('Edited');
}}
/>
</label>
<output>{text}</output>
<button disabled={!ready || saving} onClick={save}>
Save
</button>
<p>{message}</p>
</>
);
}
export default function App() {
return <DraftEditor />;
}import { useEffect, useState } from 'react';
import { atom, createStore, useAtomValue } from 'jotai';
function openDraftDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open('draft-example', 1);
let blocked = false;
request.onupgradeneeded = () => {
request.result.createObjectStore('drafts');
};
request.onblocked = () => {
blocked = true;
reject(new Error('Close other tabs to upgrade storage'));
};
request.onerror = () => reject(request.error);
request.onsuccess = () => {
const db = request.result;
if (blocked) {
db.close();
return;
}
db.onversionchange = () => db.close();
resolve(db);
};
});
}
async function storedDraft(mode, text) {
const db = await openDraftDB();
try {
return await new Promise((resolve, reject) => {
const tx = db.transaction('drafts', mode);
const store = tx.objectStore('drafts');
const request = mode === 'readonly' ? store.get('draft') : store.put(text, 'draft');
tx.oncomplete = () => resolve(request.result);
tx.onabort = () => reject(tx.error || new Error('Storage aborted'));
});
} finally {
db.close();
}
}
const draftAtom = atom('');
const store = createStore();
const hydrated = storedDraft('readonly').then((saved) => {
if (saved === undefined) return;
if (typeof saved !== 'string') throw new Error('Invalid draft');
const value = saved;
store.set(draftAtom, value);
});
// Observe errors immediately, even before the component mounts.
hydrated.catch(() => {});
function DraftEditor() {
const text = useAtomValue(draftAtom, { store });
const [ready, setReady] = useState(false);
const [saving, setSaving] = useState(false);
const [message, setMessage] = useState('Loading');
useEffect(() => {
let active = true;
hydrated.then(
() => {
if (active) {
setReady(true);
setMessage('Ready');
}
},
() => {
if (active) setMessage('Storage error');
},
);
return () => {
active = false;
};
}, []);
async function save() {
setSaving(true);
try {
await storedDraft('readwrite', store.get(draftAtom));
setMessage('Saved');
} catch {
setMessage('Save failed');
} finally {
setSaving(false);
}
}
return (
<>
<label>
Draft
<textarea
value={text}
disabled={!ready || saving}
onChange={(event) => {
const value = event.target.value;
store.set(draftAtom, value);
setMessage('Edited');
}}
/>
</label>
<output>{text}</output>
<button disabled={!ready || saving} onClick={save}>
Save
</button>
<p>{message}</p>
</>
);
}
export default function App() {
return <DraftEditor />;
}restate persistedRestate নিজে থেকে ফিরিয়ে আনে, আর useStatus সংরক্ষণ, অসংরক্ষিত পরিবর্তন ও ত্রুটি জানায়।
import { persistedRestate } from '@kkwaenggwari/restate';
const useDraft = persistedRestate('', {
key: 'draft',
validate: (value) => typeof value === 'string',
});
export default function App() {
const text = useDraft();
const { dirty, operation, error } = useDraft.useStatus();
const saving = operation === 'save';
const message = error ? 'Save failed' : saving ? 'Saving…' : dirty ? 'Edited' : 'Saved';
return (
<>
<label>
Draft
<textarea value={text} onChange={(event) => useDraft.set(event.target.value)} />
</label>
<output>{text}</output>
<button disabled={saving} onClick={() => useDraft.save().catch(() => {})}>
Save
</button>
<p>{message}</p>
</>
);
}অন্য সংস্করণগুলো একই IndexedDB কোড হাতে লেখে। তাদের persistence middleware বা স্টোরেজ টুল অ্যাডাপ্টার দিয়ে এটি ছোট করতে পারে; সেই কোড এখানে গোনা হয় না।
প্রতিটি ডকুমেন্টের স্টেট
দুটি ডকুমেন্ট আলাদা স্টেট রাখে, আর একটি বন্ধ করলে তার স্টেট ছেড়ে দেওয়া হয়।
Redux Toolkit হালনাগাদকে slice আর action-এ সাজায়, যা React context দিয়ে পাওয়া স্টোরে পাঠানো হয়। চমৎকার DevTools আর বড় দলের জন্য কঠোর, অনুমানযোগ্য হালনাগাদ প্রবাহ।
Zustand create() দিয়ে স্টোর হুক তৈরি করে এবং setState দিয়ে বদলায়। ছোট্ট আর নমনীয়, persistence ও DevTools-এর middleware সহ।
Jotai atom আর উদ্ভূত atom জোড়ে, চাইলে Provider-এর ভেতরে। স্টেটের অনেক ছোট টুকরোর মধ্যে সূক্ষ্ম নির্ভরতা।
import { useState } from 'react';
import { configureStore, createSlice } from '@reduxjs/toolkit';
import { Provider, useSelector } from 'react-redux';
const draft = createSlice({
name: 'draft',
initialState: '',
reducers: { edit: (_, action) => action.payload },
});
const selectionSlice = createSlice({
name: 'selection',
initialState: 0,
reducers: { next: (index) => index + 1 },
});
function createDocument() {
return {
store: configureStore({
reducer: {
draft: draft.reducer,
selection: selectionSlice.reducer,
},
}),
};
}
function Document({ doc, name }) {
const text = useSelector((state) => state.draft);
const selection = useSelector((state) => state.selection);
return (
<section>
<label>
{name}
<input
value={text}
onChange={(event) => doc.store.dispatch(draft.actions.edit(event.target.value))}
/>
</label>
<output>{text}</output>
<button onClick={() => doc.store.dispatch(selectionSlice.actions.next())}>
Selection: {selection}
</button>
</section>
);
}
function DocumentSlot({ name }) {
const [doc] = useState(createDocument);
return (
<Provider store={doc.store}>
<Document doc={doc} name={name} />
</Provider>
);
}
export default function App() {
const [leftOpen, setLeftOpen] = useState(true);
return (
<>
<button onClick={() => setLeftOpen((open) => !open)}>
{leftOpen ? 'Close left' : 'Open left'}
</button>
{leftOpen && <DocumentSlot name="Left" />}
<DocumentSlot name="Right" />
</>
);
}import { useState } from 'react';
import { useStore } from 'zustand';
import { createStore } from 'zustand/vanilla';
function createDocument() {
return {
draft: createStore(() => ({ text: '' })),
selection: createStore(() => ({ index: 0 })),
};
}
function Document({ doc, name }) {
const text = useStore(doc.draft, (state) => state.text);
const selection = useStore(doc.selection, (state) => state.index);
return (
<section>
<label>
{name}
<input
value={text}
onChange={(event) => doc.draft.setState({ text: event.target.value })}
/>
</label>
<output>{text}</output>
<button onClick={() => doc.selection.setState((s) => ({ index: s.index + 1 }))}>
Selection: {selection}
</button>
</section>
);
}
function DocumentSlot({ name }) {
const [doc] = useState(createDocument);
return <Document doc={doc} name={name} />;
}
export default function App() {
const [leftOpen, setLeftOpen] = useState(true);
return (
<>
<button onClick={() => setLeftOpen((open) => !open)}>
{leftOpen ? 'Close left' : 'Open left'}
</button>
{leftOpen && <DocumentSlot name="Left" />}
<DocumentSlot name="Right" />
</>
);
}import { useState } from 'react';
import { atom, createStore, useAtomValue } from 'jotai';
const draftAtom = atom('');
const selectionAtom = atom(0);
function createDocument() {
return { store: createStore() };
}
function Document({ doc, name }) {
const text = useAtomValue(draftAtom, { store: doc.store });
const selection = useAtomValue(selectionAtom, { store: doc.store });
return (
<section>
<label>
{name}
<input value={text} onChange={(event) => doc.store.set(draftAtom, event.target.value)} />
</label>
<output>{text}</output>
<button onClick={() => doc.store.set(selectionAtom, (n) => n + 1)}>
Selection: {selection}
</button>
</section>
);
}
function DocumentSlot({ name }) {
const [doc] = useState(createDocument);
return <Document doc={doc} name={name} />;
}
export default function App() {
const [leftOpen, setLeftOpen] = useState(true);
return (
<>
<button onClick={() => setLeftOpen((open) => !open)}>
{leftOpen ? 'Close left' : 'Open left'}
</button>
{leftOpen && <DocumentSlot name="Left" />}
<DocumentSlot name="Right" />
</>
);
}restate useRestateScope স্টোর তৈরি করে এবং আনমাউন্টের পরে ধ্বংস করে।
import { useState } from 'react';
import { restate, useRestateScope } from '@kkwaenggwari/restate';
function Document({ name }) {
const doc = useRestateScope((scope) => ({
useDraft: restate('', { scope }),
useSelection: restate(0, { scope }),
}));
const text = doc.useDraft();
const selection = doc.useSelection();
return (
<section>
<label>
{name}
<input value={text} onChange={(event) => doc.useDraft.set(event.target.value)} />
</label>
<output>{text}</output>
<button onClick={() => doc.useSelection.set((n) => n + 1)}>Selection: {selection}</button>
</section>
);
}
export default function App() {
const [leftOpen, setLeftOpen] = useState(true);
return (
<>
<button onClick={() => setLeftOpen((open) => !open)}>
{leftOpen ? 'Close left' : 'Open left'}
</button>
{leftOpen && <Document name="Left" />}
<Document name="Right" />
</>
);
}Jotai-এর Provider বা স্টোর আর Zustand-এর vanilla স্টোরও স্টেট আলাদা করতে পারে; সেগুলো আপনি নিজে তৈরি করে জোড়েন।
এই সংখ্যাগুলো কীভাবে গোনা হয়
দেখানো কোডের খালি নয় এমন লাইন আর প্রতিটি স্টেট লাইব্রেরি থেকে ইমপোর্ট করা নাম (React ইমপোর্ট গোনা হয় না)। প্রতিটি সংস্করণ একই UI, একই স্টোরেজ আর একই লোডিং ও ত্রুটি অবস্থা রেন্ডার করে। restate-এর উদাহরণ অ্যাপের এন্ট্রি পয়েন্টে await restateManager.ready ধরে নেয়। উৎসগুলো প্রতিটি প্রজেক্টের অফিশিয়াল ডকুমেন্টেশনের সঙ্গে মিলিয়ে দেখা:
- Redux Toolkit — Redux Toolkit · Quick Start, React Redux · useSelector, Redux Toolkit · configureStore. যখন একটি যাচাইযোগ্য action লগ আর বড় middleware ইকোসিস্টেম সবচেয়ে জরুরি, তখন এটি বেছে নিন
- Zustand — Zustand · Introduction, Zustand · persist. যখন সবচেয়ে ছোট কোর চান আর middleware জুড়তে পছন্দ করেন, তখন এটি বেছে নিন
- Jotai — Jotai · atom, Jotai · Storage, Jotai · Provider. যখন আপনার স্টেট স্বভাবতই ছোট উদ্ভূত মানের একটি গ্রাফ, তখন এটি বেছে নিন
02 — দ্রুত
শুধু বদলানোটুকু রেন্ডার করুন। একবার লিখুন।
দুটি প্যানেলই আপনার ব্রাউজারে সত্যিকারের restate প্যাকেজ চালায়। ক্লিক করুন, টাইপ করুন আর কাউন্টার দেখুন।
রেন্ডার ম্যাপ
একই চারটি ফিল্ড, দুইভাবে পড়া। একটি ফিল্ড বদলে রেন্ডার গুনুন।
- নাম
- থিম
- কাউন্টার
- খসড়া
- নাম
- থিম
- কাউন্টার
- খসড়া
একত্রে লেখা
autoSave টাইপিংয়ে বিরতির জন্য অপেক্ষা করে তারপর একবার লেখে। পরপর save() কল সারিতে একটিই লেখা ভাগ করে।
persistedRestate('', { key: 'note', autoSave: { delayMs: 400 } });সংখ্যাগুলো এই পাতার সত্যিকারের React রেন্ডার আর লেখা গোনা স্টোরেজ অ্যাডাপ্টার থেকে আসে। অন্য লাইব্রেরিও মান বাছতে পারে; এখানে বাছাই কেবল পড়ার একটি উপায়।
03 — স্থিতিশীল
স্টোরেজ ব্যর্থ হলেও আপনার ডেটা জায়গায় থাকে।
স্টেট হারায় ঠিক সংরক্ষণের সময়: ধীর ডিস্ক, আটকানো স্টোরেজ, বন্ধ ট্যাব। এগুলো restate 1.0-এর নিশ্চয়তা, পরামর্শ নয়।
- 01
সংরক্ষণ ক্রমানুসারে হয়
প্রতিটি স্টোর স্টোরেজের কাজ একটার পর একটা চালায়, তাই ধীর সংরক্ষণ কখনো পুরোনো মানকে নতুনটির ওপর লিখতে দেয় না।
save() · restore() - 02
অপেক্ষমাণ সংরক্ষণ সর্বশেষ মান নেয়
অন্য সংরক্ষণ সারিতে থাকাকালীন save() কল করলে সেটি তার সঙ্গে যুক্ত হয়, বাড়তি লেখা জমায় না।
save(): Promise<T> - 03
না-পড়া রেকর্ড কখনো ভুল করে প্রতিস্থাপিত হয় না
কোনো রেকর্ড লোড না হলে autoSave আর একসঙ্গে সংরক্ষণ সেটি ছোঁয় না এবং LOAD_FAILED জানায়। শুধু স্পষ্ট save() সেটি প্রতিস্থাপন করতে পারে।
LOAD_FAILED - 04
আনমাউন্টের পরেও সংরক্ষণ শেষ হয়
আনমাউন্টের আগে চাওয়া সংরক্ষণ শেষ হয়। ধ্বংস হওয়া স্টোরে পরের লেখা উপেক্ষিত হয়, যেমন আনমাউন্টের পরে React-এর স্টেট।
useRestateScope - 05
স্টোরেজ নেই? অ্যাপ তবুও চালু হয়
সার্ভার রেন্ডারিংয়ের সময় বা স্টোরেজ আটকানো থাকলে স্টোরগুলো মেমরিতে চলতে থাকে এবং জানায় যে স্টোরেজ পাওয়া যাচ্ছে না।
status().available - 06
ত্রুটি জানায় কী আর কোথায়
প্রতিটি restate ত্রুটিতে টাইপ করা কোড আর তার রেকর্ডের key থাকে, যাতে আপনি সঠিক বার্তা দেখাতে পারেন।
error.code · error.key
আপনার অ্যাপের সঙ্গে বাড়ে
ঘোষণা বদলান। কম্পোনেন্ট যেমন আছে রাখুন।
সংরক্ষণ, স্বয়ংক্রিয় সংরক্ষণ আর যাচাই একই স্টোরের বিকল্প। ডান দিকের কম্পোনেন্ট কখনো বদলায় না।
মেমরি
মেমরিতে ভাগ করা মান।
import { restate } from '@kkwaenggwari/restate'; export const useDraft = restate('');সংরক্ষিত
নিজে থেকে ফিরে আসে; save() কল করলে লেখা হয়।
import { persistedRestate } from '@kkwaenggwari/restate'; export const useDraft = persistedRestate('', { key: 'draft' });স্বয়ংক্রিয় সংরক্ষণ
টাইপিংয়ে 500 ms বিরতির পরে একবার লেখা হয়।
import { persistedRestate } from '@kkwaenggwari/restate'; export const useDraft = persistedRestate('', { key: 'draft', autoSave: { delayMs: 500 }, });যাচাইসহ
পুরোনো রেকর্ড ব্যবহারের আগে মাইগ্রেট ও যাচাই করা হয়।
import { persistedRestate } from '@kkwaenggwari/restate'; export const useDraft = persistedRestate('', { key: 'draft', autoSave: { delayMs: 500 }, version: 2, migrate: (value) => String(value ?? ''), validate: (value) => typeof value === 'string', });
কম্পোনেন্ট — প্রতিটি ধাপে একই
function Editor() {
const text = useDraft();
return <textarea value={text} onChange={(event) => useDraft.set(event.target.value)} />;
}যেখানে React চলে সেখানেই চলে
প্রতিটি React পরিবেশের জন্য একটি প্যাকেজ।
react >=16.8.0React
React 16.8 ও নতুন সংস্করণে, ব্রাউজারে আর সার্ভার রেন্ডারিংয়ের সময় কাজ করে।
react-native-mmkv >=2.0.0React Native
একই ইমপোর্ট। react-native-mmkv ইনস্টল করলেই সংরক্ষণ MMKV ব্যবহার করে।
SSRসার্ভার রেন্ডারিং
কম্পোনেন্টের স্টোর props থেকে সার্ভার স্ন্যাপশট নেয় এবং হাইড্রেশনের পরে লোড শুরু করে।
index.d.tsTypeScript
ডিক্লারেশন সঙ্গে আসে। সিলেক্টর, স্ট্যাটাস ফিল্ড আর ত্রুটি কোড টাইপ করা।
শুরু
এক মিনিটে আপনার প্রথম ভাগ করা স্টেট।
ইনস্টল করুন, ঘোষণা করুন, ব্যবহার করুন। অ্যাপের দরকার হলে সংরক্ষণ যোগ করুন।
- 01ইনস্টল করুন
npm install @kkwaenggwari/restate - 02স্টোর ঘোষণা করুন
export const useCart = restate({ items: 0, total: 0 }); - 03যেকোনো কম্পোনেন্টে পড়ুন
const items = useCart((cart) => cart.items);
KKWAENGGWARI