useSyncExternalStore Hook — React-ի կապը արտաքին store-երի հետ
React 18-ում ավելացավ նոր hook՝ useSyncExternalStore, որը նախատեսված է React-ին անվտանգ և կայուն ձևով միացնելու համար ցանկացած արտաքին store կամ տվյալների աղբյուրին։ Այն ապահովում է, որ React կոմպոնենտը համաժամեցվի տվյալների վերջին արժեքի հետ՝ առանց race condition-ների։
Արդյունքում՝ React-ը միշտ render է անում consistent տվյալներով, նույնիսկ եթե store-ը փոխվել է render-ի ընթացքում։ Սա այն hook-ն է, որի վրա կառուցված են ժամանակակից գրադարանները՝ Redux, Zustand, jotai, Recoil և այլն։
🔹 Syntax
const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)
- subscribe — ֆունկցիա, որը տեղեկացնում է React-ին, երբ store-ը փոխվում է։
- getSnapshot — վերադարձնում է store-ի ընթացիկ արժեքը։
- getServerSnapshot — ընտրովի՝ օգտագործվում է SSR-ի ժամանակ server-side արժեք տալու համար։
📍 Օրինակ 1 — Պարզ custom store
Ստեղծենք փոքր JavaScript store, որը պահում է արժեք և թույլ է տալիս subscribe անել փոփոխություններին։
// store.js
let count = 0;
let listeners = new Set();
export const store = {
getSnapshot() {
return count;
},
subscribe(callback) {
listeners.add(callback);
return () => listeners.delete(callback);
},
increment() {
count++;
listeners.forEach((cb) => cb());
}
};
Այստեղ մենք ունենք պարզ counter store, որը կարող է․ ✅ պահել արժեքը (`count`) ✅ տեղեկացնել բոլոր subscriber-ներին, երբ count-ը փոխվում է
Օգտագործում React կոմպոնենտում
// Counter.js
import React from 'react';
import { store } from './store';
import { useSyncExternalStore } from 'react';
export default function Counter() {
const count = useSyncExternalStore(
store.subscribe,
store.getSnapshot
);
return (
<div>
<h2>Count: {count}</h2>
<button onClick={store.increment}>+1</button>
</div>
);
}
Այս դեպքում React-ը միշտ ստանում է վերջին արժեքը `store`-ից և rerender է անում միայն այն ժամանակ, երբ store-ը իրոք փոփոխվում է։ Սա շատ ավելի արդյունավետ և ապահով է, քան օգտագործել `useEffect` կամ `useState` store-ի սինխրոնացման համար։
📍 Ինչու է պետք useSyncExternalStore
Նախկինում շատ գրադարաններ (օր. Redux-ի հին տարբերակներ) օգտագործում էին useEffect՝ store-ից տվյալներ վերցնելու և React-ին տեղեկացնելու համար։ Սակայն concurrent rendering-ի ժամանակ դա առաջացնում էր race condition՝ երբ React-ը render էր անում հին արժեքներով։
useSyncExternalStore-ը լուծում է այդ խնդիրը՝ երաշխավորելով, որ render-ի ընթացքում React-ը միշտ կօգտագործի վերջին consistent snapshot-ը։
📍 Օրինակ 2 — Կապ socket-ի կամ API-ի հետ
Օրինակ՝ ունես websocket, որը ստանում է real-time տվյալներ։ useSyncExternalStore-ը կարող է օգտագործվել, որպեսզի React կոմպոնենտը ստանա վերջին արժեքները՝ առանց rerender lag-ի։
import React, { useSyncExternalStore } from 'react';
function createSocketStore() {
let data = 'waiting...';
let listeners = new Set();
const ws = new WebSocket('wss://example.com/socket');
ws.onmessage = (e) => {
data = e.data;
listeners.forEach((cb) => cb());
};
return {
subscribe(cb) {
listeners.add(cb);
return () => listeners.delete(cb);
},
getSnapshot() {
return data;
}
};
}
const socketStore = createSocketStore();
export default function LiveData() {
const value = useSyncExternalStore(
socketStore.subscribe,
socketStore.getSnapshot
);
return <div>📡 Վերջին տվյալը՝ {value}</div>;
}
Այստեղ React-ը ստանում է live տվյալներ socket-ից, բայց միշտ consistent — առանց վտանգի, որ render-ը կհասնի հին արժեքին։
📍 Օրինակ 3 — SSR-ում getServerSnapshot
Եթե SSR նախագծում (օր. Next.js) օգտագործում ես store, կարող ես տալ server-side snapshot — օրինակ default արժեքը։
const count = useSyncExternalStore(
store.subscribe,
store.getSnapshot,
() => 0 // Server-side default
);
Այսպես React-ը չի գանգատվի, որ server և client snapshot-ները տարբեր են hydration-ի ժամանակ։
⚙️ Լավագույն պրակտիկաներ
- Օգտագործիր այն, երբ React-ը պետք է համաժամեցվի արտաքին տվյալների աղբյուրի հետ։
- Մի օգտագործիր useEffect կամ useState store sync-ի համար՝ դա հնացած մոտեցում է concurrent rendering-ում։
- Եթե գրադարան ես ստեղծում (օր. custom state manager), կառուցիր այն useSyncExternalStore-ի վրա։
- Ստեղծիր կայուն subscribe/getSnapshot զույգեր՝ race condition-ից խուսափելու համար։
🎯 Վարժություն (փորձիր ինքդ)
Գրիր փոքր store, որը․
- Պահում է counter արժեք։
- Ունի subscribe(callback) և getSnapshot() մեթոդներ։
- React կոմպոնենտում օգտագործիր useSyncExternalStore՝ ցուցադրելու և փոփոխելու համար։
Փորձիր նույնը անել useEffect/useState տարբերակով և տես տարբերությունը՝ render consistency-ի և rerender արագության մեջ։
📘 Ամփոփում
- useSyncExternalStore — React-ի hook է արտաքին տվյալների աղբյուրների հետ consistent կապի համար։
- Օգտագործվում է Redux-ի, Zustand-ի, Firebase-ի և socket-ների նման state համակարգերում։
- Համապատասխանում է concurrent rendering-ի նոր տրամաբանությանը։
- Ապահովում է consistent snapshot և կանխում race condition-ները։