useSyncExternalStore - 과소평가된 React API
useSyncExternalStore - 과소평가된 React API
외부 데이터 소스를 구독하기 위한 새로운 React 18 후크인 useSyncExternalStore()
에 대해 들어보셨을 것입니다. 선택기 시스템을 구현하기 위해 Redux과 같은 상태 관리 라이브러리에서 내부적으로 자주 사용됩니다.
그러나 자신의 애플리케이션 코드에서 useSyncExternalStore()
를 사용하는 것은 어떻습니까?
이 대화형 기사에서는 불필요한 재렌더링을 트리거하는 초과 반환 React 후크라는 문제를 제시하고자 합니다. useSyncExternalStore()
가 어떻게 좋은 해결책이 될 수 있는지 살펴보겠습니다.
💡 Pro tip: read the original article on ThisWeekInReact.com: it is interactive! 😜
오버 리턴 후크
React-Router에서 useLocation()
의 문제를 설명하겠습니다.
이 후크는 많은 속성( pathname
, hash
, search
...)이 있는 객체를 반환하지만 모든 속성을 읽지 못할 수도 있습니다. 후크를 호출하기만 하면 이러한 속성이 업데이트될 때 다시 렌더링이 트리거됩니다.
이 앱을 고려해 보겠습니다.
function CurrentPathname() {
const { pathname } = useLocation();
return <div>{pathname}</div>;
}
function CurrentHash() {
const { hash } = useLocation();
return <div>{hash}</div>;
}
function Links() {
return (
<div>
<Link to="#link1">#link1</Link>
<Link to="#link2">#link2</Link>
<Link to="#link3">#link3</Link>
</div>
);
}
function App() {
return (
<div>
<CurrentPathname />
<CurrentHash />
<Links />
</div>
);
}
해시 링크를 클릭하면 CurrentPathname
속성 😅을 사용하지 않더라도 hash
구성 요소가 다시 렌더링됩니다.
💡 Whenever a hook returns data that you don't display, think about React re-renders. If you don't pay attention, a tiny useLocation() call added at the top of a React tree could harm your app's performance.
ℹ️ The goal is not to criticize React-Router, but rather to illustrate the problem. useLocation() is just a good pragmatic candidate to create this interactive article. Your own React hooks and other third-party libraries might also over-return.
구조에 useSyncExternalStore?
official documentation 말한다:
useSyncExternalStore is a hook recommended for reading and subscribing from external data sources in a way that’s compatible with concurrent rendering features like selective hydration and time slicing.
This method returns the value of the store and accepts three arguments:
-
subscribe
: function to register a callback that is called whenever the store changes.
-
getSnapshot
: function that returns the current value of the store.
-
getServerSnapshot
: function that returns the snapshot used during server rendering.
function useSyncExternalStore<Snapshot>(
subscribe: (onStoreChange: () => void) => () => void,
getSnapshot: () => Snapshot,
getServerSnapshot?: () => Snapshot
): Snapshot;
약간 추상적인 느낌입니다. 이것은beta doc page 좋은 예를 제공합니다.
function subscribe(callback) {
window.addEventListener("online", callback);
window.addEventListener("offline", callback);
return () => {
window.removeEventListener("online", callback);
window.removeEventListener("offline", callback);
};
}
function useOnlineStatus() {
return useSyncExternalStore(
subscribe,
() => navigator.onLine,
() => true
);
}
function ChatIndicator() {
const isOnline = useOnlineStatus();
// ...
}
브라우저 기록도 외부 데이터 소스로 간주될 수 있음이 밝혀졌습니다. React-Router와 함께 useSyncExternalStore
를 사용하는 방법을 알아봅시다!
useHistorySelector() 구현하기
React-Router는 연결에 필요한 모든 것을 노출합니다useSyncExternalStore
.
💡 Pro tip: read the original article on ThisWeekInReact.com: it is interactive! 😜
function CurrentPathname() {
const { pathname } = useLocation();
return <div>{pathname}</div>;
}
function CurrentHash() {
const { hash } = useLocation();
return <div>{hash}</div>;
}
function Links() {
return (
<div>
<Link to="#link1">#link1</Link>
<Link to="#link2">#link2</Link>
<Link to="#link3">#link3</Link>
</div>
);
}
function App() {
return (
<div>
<CurrentPathname />
<CurrentHash />
<Links />
</div>
);
}
💡 Whenever a hook returns data that you don't display, think about React re-renders. If you don't pay attention, a tiny useLocation() call added at the top of a React tree could harm your app's performance.
ℹ️ The goal is not to criticize React-Router, but rather to illustrate the problem. useLocation() is just a good pragmatic candidate to create this interactive article. Your own React hooks and other third-party libraries might also over-return.
useSyncExternalStore is a hook recommended for reading and subscribing from external data sources in a way that’s compatible with concurrent rendering features like selective hydration and time slicing.
This method returns the value of the store and accepts three arguments:
-
subscribe
: function to register a callback that is called whenever the store changes. -
getSnapshot
: function that returns the current value of the store. -
getServerSnapshot
: function that returns the snapshot used during server rendering.
function useSyncExternalStore<Snapshot>(
subscribe: (onStoreChange: () => void) => () => void,
getSnapshot: () => Snapshot,
getServerSnapshot?: () => Snapshot
): Snapshot;
function subscribe(callback) {
window.addEventListener("online", callback);
window.addEventListener("offline", callback);
return () => {
window.removeEventListener("online", callback);
window.removeEventListener("offline", callback);
};
}
function useOnlineStatus() {
return useSyncExternalStore(
subscribe,
() => navigator.onLine,
() => true
);
}
function ChatIndicator() {
const isOnline = useOnlineStatus();
// ...
}
useHistory()
으로 브라우저 기록에 액세스history.listen(callback)
의 기록 업데이트 구독history.location
을 사용하여 현재 위치의 스냅샷에 액세스⚠️ This website uses React-Router v5: the solution will be different for React-Router v6 ( ).
useHistorySelector()
의 구현은 비교적 간단합니다.function useHistorySelector(selector) {
const history = useHistory();
return useSyncExternalStore(history.listen, () =>
selector(history)
);
}
우리 앱에서 사용해봅시다:
function CurrentPathname() {
const pathname = useHistorySelector(
(history) => history.location.pathname
);
return <div>{pathname}</div>;
}
function CurrentHash() {
const hash = useHistorySelector(
(history) => history.location.hash
);
return <div>{hash}</div>;
}
이제 위의 해시 링크를 클릭하면
CurrentPathname
구성 요소가 더 이상 다시 렌더링되지 않습니다!다른 예: scrollY
우리가 구독할 수 있는 외부 데이터 소스가 너무 많고 자체 선택기 시스템을 구현하면 React 리렌더링을 최적화할 수 있습니다.
예를 들어 페이지의
scrollY
위치를 사용하려고 한다고 가정해 보겠습니다. 이 사용자 지정 React 후크를 구현할 수 있습니다.// A memoized constant fn prevents unsubscribe/resubscribe
// In practice it is not a big deal
function subscribe(onStoreChange) {
global.window?.addEventListener("scroll", onStoreChange);
return () =>
global.window?.removeEventListener(
"scroll",
onStoreChange
);
}
function useScrollY(selector = (id) => id) {
return useSyncExternalStore(
subscribe,
() => selector(global.window?.scrollY),
() => undefined
);
}
이제 선택적 선택기와 함께 이 후크를 사용할 수 있습니다.
function ScrollY() {
const scrollY = useScrollY();
return <div>{scrollY}</div>;
}
function ScrollYFloored() {
const to = 100;
const scrollYFloored = useScrollY((y) =>
y ? Math.floor(y / to) * to : undefined
);
return <div>{scrollYFloored}</div>;
}
페이지를 스크롤하여 위의 구성요소가 어떻게 다시 렌더링되는지 확인하시겠습니까? 하나는 다른 것보다 덜 렌더링됩니다!
💡 When you don't need a
scrollY
1 pixel precision level, returning a wide range value such asscrollY
can also be considered as over-returning. Consider returning a narrower value.
For example: auseResponsiveBreakpoint()
hook that only returns a limited set of values (small
,medium
orlarge
) will be more optimized than auseViewportWidth()
hook.
If a React component only handleslarge
screens differently, you can create an even narroweruseIsLargeScreen()
hook returning a boolean.
결론
이 기사가
useSyncExternalStore()
를 다시 살펴보게 되기를 바랍니다. 나는 이 훅이 현재 React 생태계에서 잘 사용되지 않고 있으며 좀 더 주의를 기울일 필요가 있다고 생각합니다. 구독할 수 있는 많은 외부 데이터 원본이 있습니다.아직 React 18로 업그레이드하지 않았다면 현재 이전 버전에서 이미 사용할 수 있는 npmuse-sync-external-store shim이 있습니다. 기본이 아닌 메모화된 값을 반환해야 하는 경우를 대비한 내보내기
use-sync-external-store/with-selector
도 있습니다.이와 같은 기사를 더 보려면 내 뉴스레터This Week In React를 구독하십시오.
Reference
이 문제에 관하여(useSyncExternalStore - 과소평가된 React API), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/sebastienlorber/usesyncexternalstore-the-underrated-react-api-3ad텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)