영원한 우리 형 kent c dodds의 epic-react에서 배운 거 정리
flushSync는 React에 제공된 콜백 내부의 모든 업데이트를 동기적으로 처리하도록 강제합니다. DOM이 즉시 업데이트되는 것을 보장합니다.
React18 부터 Automatic batching 적용되면서 렌더링 비용을 감소하는 이점을 얻고 있다.
하지만, 상태 변화로 인해 DOM의 즉각적인 업데이트 후 추가 작업을 진행해야 하는 경우가 있다(ref...) 그럴 때 사용하는 flushSync
함수
가령 위와 같은 상황이 있다고 가정하자. isEdit
일 때는 input이고, onSubmit을 이후 isEdit
이 false
가 되어 button 이 렌더링 된다.
input에 value를 작성하고 onSubmit을 실행한 후, button을 focus하고 싶다고 하자.
하지만 state가 즉각적으로 업데이트 되지 않기 때문에 아래 buttonRef는 여전히 null 이고 buttonRef.current.focus()
는 작동하지 않는다.
코드는 대략 아래와 같다.
위와 같은 문제를 해결하기 위해서 requestAnimationFrame
혹은 setTimeout
과 같은 인위적으로 DOM 렌더링 타이밍을 맞추는 방법도 있겠지만, 퍼포먼스 등 여러 문제가 있다.
그래서 사용하는 것이 flushSync
flushSync
을 통해 즉각적으로 상태가 변경되어 업데이트 된 DOM에서 Ref로 접근하여 focus까지 이루어진다. 하지만 flushSync
는 리액트의 배치 업데이트를 위반하기(?) 때문에 'DOM 렌더링 후 즉각 Ref 접근'의 경우에만 사용한다고 한다.
useSyncExternalStore
은 리액트18부터 동시성이 추가되면서 Tearing 현상이 발생하게 되었는데, 이를 해결하기 위해 추가된 api다.
외부 저장소, 즉 리액트를 제외한 모든 서드파티 라이브러리, 브라우저 api 등과 동기화를 맞추는 역할을 한다.
가령, use-media-query
을 작성한다고 했을 때
이런 식으로 useState
와 useEffect
을 사용하여 window 객체의 matchMedia 함수를 사용해야 한다.
하지만 useSyncExternalStore
을 사용하면 좀 더 깔끔하게 작성할 수 있다.
먼저 subscribe
함수를 만든다.
subscribe
함수에선 이벤트를 등록/해제한다.
그리고 getSnapshot
을 만든다.
getSnapshot
이 반환한 값과 저장소에 저장된 값이 다르면 리액트는 리렌더링하게 된다.
위 두 함수를 useSyncExternalStore
에 인자로 넘기면 끝이다.
further
getServerSnapshot이 초기 클라이언트 렌더링에서 서버에서 반환한 것과 동일한 정확한 데이터를 반환하는지 확인하세요. 예를 들어 getServerSnapshot이 서버에서 미리 채워진 store 콘텐츠를 반환한 경우 이 콘텐츠를 클라이언트로 전송해야 합니다. 이를 수행하는 일반적인 방법 중 하나는 서버 렌더링 중에 window.MY_STORE_DATA와 같은 글로벌을 설정하는
만약, SSR을 사용한다고 가정하자.
useSyncExternalStore
의 3번째 인자로 getServerSnapshot
인자를 넘기지 않으면 아래와 같은 에러가 나온다.
하지만, matchMedia
을 useSyncExternalStore
의 이벤트로 등록하게 되면 클라이언트와 서버가 hydration 할 때의 matchMedia의 초기값을 알 수가 없기 때문에 getServerSnapshot
을 사용할 수 없다.
그런 경우에 suspense
를 사용하여 hydration가 끝날 때까지 기다리는 편이 낫다.
추가적으로 해당 에러가 신경쓰인다면 아래와 같이 onRecoverableError
를 통해 에러메시지를 무시할 수도 있다.
참고: epic-react