React ಮತ್ತು React Native ಗಾಗಿ ಸ್ಟೇಟ್ · 1.0

ಸ್ಟೇಟ್ ಹಂಚಿಕೊಳ್ಳಿ.ಒಂದು ಹುಕ್.ಮುಗಿಯಿತು.

ಒಂದೇ ಸಾಲಿನಲ್ಲಿ ಸ್ಟೋರ್ ಘೋಷಿಸಿ ಮತ್ತು Provider, action ಅಥವಾ reducer ಇಲ್ಲದೆ ಯಾವುದೇ ಕಾಂಪೊನೆಂಟ್‌ನಲ್ಲಿ ಬಳಸಿ. ಕಾಂಪೊನೆಂಟ್ ತಾನು ಆಯ್ಕೆಮಾಡಿದ ಮೌಲ್ಯ ಬದಲಾದಾಗ ಮಾತ್ರ ಮತ್ತೆ ರೆಂಡರ್ ಆಗುತ್ತದೆ; ಉಳಿಕೆ ನೀವು ನಿರ್ಧರಿಸಿದಾಗ ನಡೆಯುತ್ತದೆ.

Terminal
npm install @kkwaenggwari/restate

ನೇರ — ನಿಜವಾದ restate ಸ್ಟೋರ್

useCart.set(cart => ({ ...cart, items: cart.items + 1, total: cart.total + 12 }))

items ಮತ್ತು total ಬದಲಾಗುತ್ತವೆ: ಮೂರು ಕಾರ್ಡ್‌ಗಳು ರೆಂಡರ್ ಆಗುತ್ತವೆ, ಗಿಫ್ಟ್ ಕಾರ್ಡ್ ಅಲುಗಾಡದೆ ಉಳಿಯುತ್ತದೆ.

01 — ಸುಲಭ

ಅದೇ ವೈಶಿಷ್ಟ್ಯ, ಕಡಿಮೆ ಪರಿಕಲ್ಪನೆಗಳು.

ಒಂದೇ ಕೆಲಸದ ನಾಲ್ಕು ಅನುಷ್ಠಾನಗಳು ಅಕ್ಕಪಕ್ಕದಲ್ಲಿ. ಸಂಖ್ಯೆಗಳು ಇಲ್ಲಿ ತೋರಿಸಿದ ಕೋಡ್‌ನಿಂದ ಬರುತ್ತವೆ ಮತ್ತು ಈ ಸೈಟ್ build ಆಗುವಾಗ ಅಳೆಯಲಾಗುತ್ತದೆ.

ಹಂಚಿಕೊಂಡ ಕೌಂಟರ್

ಎರಡು ಕಾಂಪೊನೆಂಟ್‌ಗಳು ಒಂದೇ ಸಂಖ್ಯೆಯನ್ನು ಓದಿ ನವೀಕರಿಸುತ್ತವೆ.

Redux Toolkit ನವೀಕರಣಗಳನ್ನು slice ಮತ್ತು action ಗಳಾಗಿ ಸಂಘಟಿಸಿ, React context ಮೂಲಕ ಒದಗಿಸಿದ ಸ್ಟೋರ್‌ಗೆ ಕಳುಹಿಸುತ್ತದೆ. ಅತ್ಯುತ್ತಮ DevTools ಮತ್ತು ದೊಡ್ಡ ತಂಡಗಳಿಗೆ ಕಟ್ಟುನಿಟ್ಟಾದ, ಊಹಿಸಬಹುದಾದ ನವೀಕರಣ ಹರಿವು.

Zustand create() ಮೂಲಕ ಸ್ಟೋರ್ ಹುಕ್ ರಚಿಸಿ setState ಮೂಲಕ ಬದಲಿಸುತ್ತದೆ. ಅತಿ ಚಿಕ್ಕ ಮತ್ತು ನಮ್ಯ, persistence ಮತ್ತು DevTools ಗಾಗಿ middleware ನೊಂದಿಗೆ.

Jotai atom ಮತ್ತು ವ್ಯುತ್ಪನ್ನ atom ಗಳನ್ನು ಜೋಡಿಸುತ್ತದೆ, ಐಚ್ಛಿಕವಾಗಿ Provider ಒಳಗೆ. ಅನೇಕ ಸಣ್ಣ ಸ್ಟೇಟ್ ತುಣುಕುಗಳ ನಡುವಿನ ಸೂಕ್ಷ್ಮ ಅವಲಂಬನೆಗಳು.

Redux Toolkit29 ಸಾಲುಗಳು · 5 API
JavaScript / JSX
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>
  );
}
Zustand16 ಸಾಲುಗಳು · 1 API
JavaScript / JSX
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 />
    </>
  );
}
Jotai13 ಸಾಲುಗಳು · 2 API
JavaScript / JSX
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 ಒಂದೇ ಘೋಷಣೆ ಸ್ಟೋರ್ ಮತ್ತು ಹುಕ್ ಎರಡೂ ಆಗುತ್ತದೆ.

restate13 ಸಾಲುಗಳು · 1 API
JavaScript / JSX
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 ಒಳಗೆ. ಅನೇಕ ಸಣ್ಣ ಸ್ಟೇಟ್ ತುಣುಕುಗಳ ನಡುವಿನ ಸೂಕ್ಷ್ಮ ಅವಲಂಬನೆಗಳು.

Redux Toolkit26 ಸಾಲುಗಳು · 4 API
JavaScript / JSX
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>
  );
}
Zustand17 ಸಾಲುಗಳು · 1 API
JavaScript / JSX
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>
    </>
  );
}
Jotai19 ಸಾಲುಗಳು · 3 API
JavaScript / JSX
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 ಕೊಡಿ.

restate17 ಸಾಲುಗಳು · 1 API
JavaScript / JSX
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 ಒಳಗೆ. ಅನೇಕ ಸಣ್ಣ ಸ್ಟೇಟ್ ತುಣುಕುಗಳ ನಡುವಿನ ಸೂಕ್ಷ್ಮ ಅವಲಂಬನೆಗಳು.

Redux Toolkit84 ಸಾಲುಗಳು · 4 API
JavaScript / JSX
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>
  );
}
Zustand79 ಸಾಲುಗಳು · 1 API
JavaScript / JSX
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 />;
}
Jotai80 ಸಾಲುಗಳು · 3 API
JavaScript / JSX
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 ಉಳಿಕೆ, ಉಳಿಸದ ಬದಲಾವಣೆಗಳು ಮತ್ತು ದೋಷಗಳನ್ನು ವರದಿ ಮಾಡುತ್ತದೆ.

restate18 ಸಾಲುಗಳು · 1 API
JavaScript / JSX
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 ಒಳಗೆ. ಅನೇಕ ಸಣ್ಣ ಸ್ಟೇಟ್ ತುಣುಕುಗಳ ನಡುವಿನ ಸೂಕ್ಷ್ಮ ಅವಲಂಬನೆಗಳು.

Redux Toolkit42 ಸಾಲುಗಳು · 4 API
JavaScript / JSX
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" />
    </>
  );
}
Zustand35 ಸಾಲುಗಳು · 2 API
JavaScript / JSX
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" />
    </>
  );
}
Jotai33 ಸಾಲುಗಳು · 3 API
JavaScript / JSX
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 ಸ್ಟೋರ್‌ಗಳನ್ನು ರಚಿಸಿ ಅನ್‌ಮೌಂಟ್ ನಂತರ ನಾಶಪಡಿಸುತ್ತದೆ.

restate27 ಸಾಲುಗಳು · 2 API
JavaScript / JSX
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 ಅಥವಾ store, Zustand ನಲ್ಲಿ vanilla store ಗಳು ಸ್ಟೇಟ್ ಪ್ರತ್ಯೇಕಿಸಬಲ್ಲವು; ಅವುಗಳನ್ನು ನೀವೇ ರಚಿಸಿ ಜೋಡಿಸುತ್ತೀರಿ.

ಈ ಸಂಖ್ಯೆಗಳನ್ನು ಹೇಗೆ ಎಣಿಸಲಾಗುತ್ತದೆ

ತೋರಿಸಿದ ಕೋಡ್‌ನ ಖಾಲಿಯಲ್ಲದ ಸಾಲುಗಳು ಮತ್ತು ಪ್ರತಿ ಸ್ಟೇಟ್ ಲೈಬ್ರರಿಯಿಂದ import ಮಾಡಿದ ಹೆಸರುಗಳು (React import ಗಳು ಎಣಿಕೆಯಲ್ಲಿಲ್ಲ). ಪ್ರತಿ ಆವೃತ್ತಿಯೂ ಅದೇ 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 ನ ಖಾತರಿಗಳು, ಸಲಹೆಗಳಲ್ಲ.

ವೈಫಲ್ಯ ಪ್ರಯೋಗಾಲಯ ತೆರೆಯಿರಿ ವಿಶ್ವಾಸಾರ್ಹತೆ ಮಾರ್ಗದರ್ಶಿ ಓದಿ
  1. 01

    ಉಳಿಕೆಗಳು ಕ್ರಮವಾಗಿ ನಡೆಯುತ್ತವೆ

    ಪ್ರತಿ ಸ್ಟೋರ್ ಸ್ಟೋರೇಜ್ ಕಾರ್ಯಗಳನ್ನು ಒಂದೊಂದಾಗಿ ಮಾಡುತ್ತದೆ, ಹೀಗಾಗಿ ನಿಧಾನವಾದ ಉಳಿಕೆ ಎಂದಿಗೂ ಹಳೆಯ ಮೌಲ್ಯದಿಂದ ಹೊಸದನ್ನು ತಿದ್ದಿಬರೆಯುವುದಿಲ್ಲ.

    save() · restore()
  2. 02

    ಬಾಕಿ ಉಳಿಕೆ ಇತ್ತೀಚಿನ ಮೌಲ್ಯವನ್ನು ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ

    ಇನ್ನೊಂದು ಉಳಿಕೆ ಸರದಿಯಲ್ಲಿರುವಾಗ save() ಕಾಲ್ ಮಾಡಿದರೆ, ಹೆಚ್ಚುವರಿ ಬರಹಗಳನ್ನು ಪೇರಿಸದೆ ಅದರೊಂದಿಗೆ ಸೇರುತ್ತದೆ.

    save(): Promise<T>
  3. 03

    ಓದದ ದಾಖಲೆಗಳು ಎಂದಿಗೂ ಆಕಸ್ಮಿಕವಾಗಿ ಬದಲಾಗುವುದಿಲ್ಲ

    ದಾಖಲೆ ಲೋಡ್ ಆಗದಿದ್ದರೆ, autoSave ಮತ್ತು ಒಟ್ಟು ಉಳಿಕೆಗಳು ಅದನ್ನು ಮುಟ್ಟದೆ LOAD_FAILED ವರದಿ ಮಾಡುತ್ತವೆ. ಸ್ಪಷ್ಟ save() ಮಾತ್ರ ಅದನ್ನು ಬದಲಿಸಬಲ್ಲದು.

    LOAD_FAILED
  4. 04

    ಅನ್‌ಮೌಂಟ್ ನಂತರವೂ ಉಳಿಕೆ ಮುಗಿಯುತ್ತದೆ

    ಅನ್‌ಮೌಂಟ್‌ಗೆ ಮೊದಲು ಕೇಳಿದ ಉಳಿಕೆಗಳು ಮುಗಿಯುತ್ತವೆ. ನಾಶವಾದ ಸ್ಟೋರ್‌ನ ಮೇಲಿನ ನಂತರದ ಬರಹಗಳನ್ನು, ಅನ್‌ಮೌಂಟ್ ನಂತರದ React ಸ್ಟೇಟ್‌ನಂತೆಯೇ, ನಿರ್ಲಕ್ಷಿಸಲಾಗುತ್ತದೆ.

    useRestateScope
  5. 05

    ಸ್ಟೋರೇಜ್ ಇಲ್ಲವೇ? ಆ್ಯಪ್ ಆದರೂ ಪ್ರಾರಂಭವಾಗುತ್ತದೆ

    ಸರ್ವರ್ ರೆಂಡರಿಂಗ್ ಸಮಯದಲ್ಲಿ ಅಥವಾ ಸ್ಟೋರೇಜ್ ತಡೆಹಿಡಿದಾಗ, ಸ್ಟೋರ್‌ಗಳು ಮೆಮೊರಿಯಲ್ಲಿ ಕೆಲಸ ಮುಂದುವರಿಸಿ ಸ್ಟೋರೇಜ್ ಲಭ್ಯವಿಲ್ಲ ಎಂದು ವರದಿ ಮಾಡುತ್ತವೆ.

    status().available
  6. 06

    ದೋಷಗಳು ಏನು, ಎಲ್ಲಿ ಎಂದು ಹೇಳುತ್ತವೆ

    ಪ್ರತಿ restate ದೋಷದಲ್ಲಿ ಟೈಪ್ ಮಾಡಲಾದ ಕೋಡ್ ಮತ್ತು ಅದರ ದಾಖಲೆಯ key ಇರುತ್ತದೆ, ಹೀಗಾಗಿ ಸರಿಯಾದ ಸಂದೇಶ ತೋರಿಸಬಹುದು.

    error.code · error.key

ನಿಮ್ಮ ಆ್ಯಪ್‌ನೊಂದಿಗೆ ಬೆಳೆಯುತ್ತದೆ

ಘೋಷಣೆಯನ್ನು ಬದಲಿಸಿ. ಕಾಂಪೊನೆಂಟ್‌ಗಳನ್ನು ಹಾಗೆಯೇ ಇಡಿ.

ಉಳಿಕೆ, ಸ್ವಯಂ-ಉಳಿಕೆ ಮತ್ತು ಪರಿಶೀಲನೆ ಒಂದೇ ಸ್ಟೋರ್‌ನ ಆಯ್ಕೆಗಳು. ಬಲಭಾಗದ ಕಾಂಪೊನೆಂಟ್ ಎಂದಿಗೂ ಬದಲಾಗುವುದಿಲ್ಲ.

  1. ಮೆಮೊರಿ

    ಮೆಮೊರಿಯಲ್ಲಿ ಹಂಚಿಕೊಂಡ ಮೌಲ್ಯ.

    import { restate } from '@kkwaenggwari/restate';
    
    export const useDraft = restate('');
  2. ಉಳಿಸಲಾಗುವ

    ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಮರುಸ್ಥಾಪನೆ; save() ಕಾಲ್ ಮಾಡಿದಾಗ ಬರೆಯಲಾಗುತ್ತದೆ.

    import { persistedRestate } from '@kkwaenggwari/restate';
    
    export const useDraft = persistedRestate('', { key: 'draft' });
  3. ಸ್ವಯಂ-ಉಳಿಕೆ

    ಟೈಪಿಂಗ್‌ನಲ್ಲಿ 500 ms ವಿರಾಮದ ನಂತರ ಒಮ್ಮೆ ಬರೆಯಲಾಗುತ್ತದೆ.

    import { persistedRestate } from '@kkwaenggwari/restate';
    
    export const useDraft = persistedRestate('', {
      key: 'draft',
      autoSave: { delayMs: 500 },
    });
  4. ಪರಿಶೀಲನೆಯೊಂದಿಗೆ

    ಹಳೆಯ ದಾಖಲೆಗಳನ್ನು ಬಳಸುವ ಮೊದಲು ಪರಿವರ್ತಿಸಿ ಪರಿಶೀಲಿಸಲಾಗುತ್ತದೆ.

    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.0

    React

    React 16.8 ಮತ್ತು ಹೊಸ ಆವೃತ್ತಿಗಳೊಂದಿಗೆ, ಬ್ರೌಸರ್‌ನಲ್ಲಿ ಮತ್ತು ಸರ್ವರ್ ರೆಂಡರಿಂಗ್ ಸಮಯದಲ್ಲಿ ನಡೆಯುತ್ತದೆ.

  • react-native-mmkv >=2.0.0

    React Native

    ಅದೇ import ಗಳು. react-native-mmkv ಇನ್‌ಸ್ಟಾಲ್ ಆದ ನಂತರ ಉಳಿಕೆ MMKV ಬಳಸುತ್ತದೆ.

  • SSR

    ಸರ್ವರ್ ರೆಂಡರಿಂಗ್

    ಕಾಂಪೊನೆಂಟ್ ಸ್ಟೋರ್‌ಗಳು props ನಿಂದ ಸರ್ವರ್ ಸ್ನ್ಯಾಪ್‌ಶಾಟ್ ಪಡೆದು hydration ನಂತರ ಲೋಡ್ ಆರಂಭಿಸುತ್ತವೆ.

  • index.d.ts

    TypeScript

    ಘೋಷಣೆಗಳು ಜೊತೆಗೆ ಬರುತ್ತವೆ. ಸೆಲೆಕ್ಟರ್‌ಗಳು, ಸ್ಥಿತಿ ಫೀಲ್ಡ್‌ಗಳು ಮತ್ತು ದೋಷ ಕೋಡ್‌ಗಳು ಟೈಪ್ ಮಾಡಲಾಗಿವೆ.

ಪ್ರಾರಂಭ

ಒಂದು ನಿಮಿಷದಲ್ಲಿ ನಿಮ್ಮ ಮೊದಲ ಹಂಚಿಕೊಂಡ ಸ್ಟೇಟ್.

ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿ, ಘೋಷಿಸಿ, ಬಳಸಿ. ಆ್ಯಪ್‌ಗೆ ಬೇಕಾದಾಗ ಉಳಿಕೆ ಸೇರಿಸಿ.

  1. 01
    ಇನ್‌ಸ್ಟಾಲೇಶನ್
    Terminal
    npm install @kkwaenggwari/restate
  2. 02
    ಸ್ಟೋರ್ ಘೋಷಿಸಿ
    JavaScript / JSX
    export const useCart = restate({ items: 0, total: 0 });
  3. 03
    ಯಾವುದೇ ಕಾಂಪೊನೆಂಟ್‌ನಲ್ಲಿ ಓದಿ
    JavaScript / JSX
    const items = useCart((cart) => cart.items);

KKWAENGGWARI

ಪ್ರಾಜೆಕ್ಟ್‌ಗಳು

ದಸ್ತಾವೇಜಿನಲ್ಲಿ ಹುಡುಕಿ

ಎಲ್ಲ ಮಾರ್ಗದರ್ಶಿಗಳ ಶೀರ್ಷಿಕೆ, ವಿವರಣೆ ಮತ್ತು ವಿಷಯದಲ್ಲಿ ಹುಡುಕಿ.