useSyncExternalStore

이지영·2024년 8월 27일

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의 데이터의 스냅샷을 반환하는 함수
    스토어가 변경디지 않은 상태에서 getSnapshot을 반복적으로 호출하면 동일한 값을 반환해야한다.
    저장소가 변경되어 반환값이 다르다면(Object.is와 비교하여 ) React는 컴포넌트를 리렌더링
  • option 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이 아닐 경우의 조건 부분도 조건이 변경될 떄마다 렌더링이 일시 중단되고 사용자는 로딩 상태를 보게된다.

❤️ 권장

  • 상태 기반 로딩 처리:
    외부 스토어에서 가져온 값을 기반으로 데이터를 비동기적으로 처리하는 것이 좋다.
    이렇게 하면 불필요한 로딩 화면을 피할 수 있다.
  • 비동기 데이터처리:
    데이터를 가져오는 로직을 별도로 처리하고, 로딩 상태를 관리하는 방법을 사용하는 것이 더 나은 접근이다. 예를들어 useState와 useEffect를 활용하여 데이터를 가져오고 로딩상태를 관리할 수 있다.

custom Hook으로 로직 추출하기

일반적으로 컴포넌트에서 직접 useSyncExternalStore을 작성하지 않는다.
대신 일반적으로 custom Hook에서 호출한다.
이렇게 하면 서로 다른 컴포넌트에서 동일한 외부 저장소를 사용할 수 있다.

예시

  • useOnlineStatus.js
  • App.js

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


0개의 댓글