usenSyncExternalStore는 외부 store을 구독할 수 있는 ReactHook
컴포넌트의 최상위 레벨에서 useSyncExternalStore을 호출하여
외부 데이터 저장소에서 값을 읽는다.
import {useSyncExternalStore} from 'react';
import {todosStore} from './todoStore.js';
function TodosApp() {
const todos = useSyncExternalStore(todosStore.subscribe,todosStore.getSnapshot);
}
store에 있는 데이터의 스냅샷을 반환
두 개의 함수를 인수로 전달
1. subscribe, getSnapshot
매개변수
subscribe : 하나의 callback 인수를 받아 store에 구독하는 함수
스토어가 변경되면 제공된 callback을 호출해야한다.
그럼 컴포넌트가 리렌더링된다.
subscribe함수는 구독을 정리하는 함수를 반환해야한다.
getSnapshot: 컴포넌트에 필요한 store의 데이터의 스냅샷을 반환하는 함수getServerSnapshot: store에 있는 데이터의 초기 스냅샷을 반환하는 함수 getSnapshot이 반환하는 store 스냅샷은 불변이어야한다.
기본 스토어에 변경 가능한 데이터가 있는 경우, 데이터가 변경된 경우, 변경 불가능한 새 스냅샷을 반환한다. 그렇지 않으면 캐시 된 마지막 스냅샷을 반환한다.
리렌더링하는 동안 다른 subscribe함수가 전달되면 React는 새로 전달된 subscribe함수를 사용하여 저장소를 다시 구독한다.
컴포넌트 외부에서 subscribe를 선언하면 이를 방지 할 수 있다.
useSyncExternalStore 가 반환한 스토어 값을 기반으로 렌더링을 일시 중단하는 것은 권장하지 않는다. 그 이유는 외부 스토어에 대한 변형을 non-blocking trnasition 업데이트로 표시할 수 없기 때문에 가장 가까운 suspensefallback을 트리거해서, 화면에 로딩 스피너로 대체하여 일반적으로 UX가 좋지 않기 때문이다.
👉 non-blocking transition :
React에서는 비동기적으로 상태를 업데이트할 수 있도록 설계되어 있다.
이를 통해 사용자 경험을 개선하고 애플리케이션이 더 매끄럽게 작동하도록 한다.
하지만 외부 스트어의 값에 따라 렌더링을 일시중단하면, React가 이 업데이트를 비동기적으로 처리할 수 없게된다.
👉 suspense와 fallback:
suspense컴포넌트는 비동기 작업이 완료될 때까지 대기하고,
그 동안에는 fallback으로 지정한 UI를 보여준다. ex) 로딩스피너
그러나 외부 스토어의 값이 변경될 때마다 이 과정을 트리거하면, 사용자는 불필요한 로딩 스피너를 보게되어 UX가 저하된다.

위의 코드를 보면 useSyncExternalStore 이 훅은 외부 스토어의 상태를 구독하여 해당 상태를 반환한다.
use(fetchItem(selectedProductId)): selectedProductId가 변경될 떄마다 fetchItem함수를 호출하여 데이터를 가져오려는 시도이다.
이 작업은 비동기적으로 진행되므로 selectedProductId가 렌더링이 일시 중단되고, React는 이를 suspense의 fallback I로 처리한다.
또한 selectedProductId가 null이 아닐 경우의 조건 부분도 조건이 변경될 떄마다 렌더링이 일시 중단되고 사용자는 로딩 상태를 보게된다.
일반적으로 컴포넌트에서 직접 useSyncExternalStore을 작성하지 않는다.
대신 일반적으로 custom Hook에서 호출한다.
이렇게 하면 서로 다른 컴포넌트에서 동일한 외부 저장소를 사용할 수 있다.
예시


useSyncExternalStore을 custom Hook으로 사용하는것을 알아보았다.