개요
useSyncExternalStore
는 외부 스토어(React 외부의 데이터 저장소)에 구독(subscribe)할 수 있게 해주는 React Hook입니다. 외부 상태 관리 라이브러리, 브라우저 API 등에서 값이 바뀔 때 컴포넌트를 동기적으로 최신 상태로 유지할 수 있습니다.
const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)
Reference
useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)
컴포넌트의 최상위에서 useSyncExternalStore
를 호출하면 외부 스토어의 값을 읽고, 변경 시 자동으로 리렌더링할 수 있습니다.
import { useSyncExternalStore } from 'react';
import { todosStore } from './todoStore.js';
function TodosApp() {
const todos = useSyncExternalStore(todosStore.subscribe, todosStore.getSnapshot);
// ...
}
파라미터
subscribe
: 콜백을 인자로 받아 스토어에 구독시키고, 언구독 함수(unsubscribe)를 반환하는 함수입니다. 스토어가 변경되면 콜백을 호출해야 합니다.getSnapshot
: 스토어의 현재 값을 반환하는 함수입니다. 값이 바뀌지 않았다면 항상 같은 값을 반환해야 하며, 값이 바뀌면 새로운 값을 반환해야 합니다.getServerSnapshot
(선택): 서버 렌더링 시 사용할 초기 스냅샷을 반환하는 함수입니다. 서버 렌더링/하이드레이션 시에만 사용됩니다.
반환값
- 스토어의 현재 스냅샷 값이 반환됩니다.
주의사항 및 Caveats
- getSnapshot이 반환하는 값은 반드시 불변(immutable)이어야 하며, 값이 바뀌지 않았다면 동일한 객체를 반환해야 합니다.
- subscribe 함수가 렌더마다 새로 만들어지면 매번 재구독이 일어나므로, subscribe는 컴포넌트 외부에 선언하거나 useCallback으로 감싸세요.
- getServerSnapshot은 서버와 클라이언트의 초기 값이 반드시 일치해야 합니다.
- store 값이 바뀌면 React가 자동으로 getSnapshot을 다시 호출하고, 값이 다르면 리렌더링합니다.
- Suspense와 함께 사용 시 UX가 좋지 않을 수 있으니 주의하세요.
사용법
외부 스토어 구독하기
import { useSyncExternalStore } from 'react';
import { todosStore } from './todoStore.js';
function TodosApp() {
const todos = useSyncExternalStore(todosStore.subscribe, todosStore.getSnapshot);
// ...
}
브라우저 API 구독 예시
function getSnapshot() {
return navigator.onLine;
}
function subscribe(callback) {
window.addEventListener('online', callback);
window.addEventListener('offline', callback);
return () => {
window.removeEventListener('online', callback);
window.removeEventListener('offline', callback);
};
}
function ChatIndicator() {
const isOnline = useSyncExternalStore(subscribe, getSnapshot);
return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;
}
커스텀 Hook으로 추출하기
import { useSyncExternalStore } from 'react';
export function useOnlineStatus() {
const isOnline = useSyncExternalStore(subscribe, getSnapshot);
return isOnline;
}
트러블슈팅
getSnapshot이 매번 새로운 객체를 반환하는 경우
- getSnapshot이 항상 새로운 객체를 반환하면 무한 루프가 발생할 수 있습니다. 값이 바뀌지 않았다면 이전 객체를 그대로 반환하세요.
subscribe가 매 렌더마다 새로 생성되는 경우
- subscribe 함수가 컴포넌트 내부에 있으면 매번 재구독이 일어납니다. subscribe는 컴포넌트 외부에 선언하거나 useCallback으로 감싸세요.
반응형