useId hook은 React 18에 새로 추가된 hook으로 유니크한 id를 만들어준다. 다음과 같은 상황에서 사용한다.
그리고 list에서 key로는 사용하면 안 되고 변경해서는 안 되는 값에는 사용하면 안 된다.
import {useId} from 'react';
const id = useId();
<label htmlFor={`${id}-firstName`}>
이름
<input id={`${id}-firstName`} />
</label>
useTransition 훅을 사용하면 상태 변화의 우선순위를 지정할 수 있다. useTransition 훅은 2개의 배열[isPending, startTransition]을 반환하는데 isPending은 작업이 지연되고 있음을 알리는 boolean이고 startTransition은 낮은 우선순위로 실행할 함수를 인자로 받는다.
import {useTransition, useState} from 'react';
function App() {
const [isPending, startTransition] = useTransition();
const [count, setCount] = useTate(0);
function handleClick() {
startTransition(() => {
setCount(c => c + 1);
});
}
return (
<div>
{isPending && <Spinner />}
<button onClick={handleClick}>{count}</button>
</div>
);
}
위 코드는 클릭할 때마다 일어나는 count에 대한 상태 업데이트를 낮은 우선순위로 실행한다. 더 중요한 이벤트가 있는 경우 count의 업데이트를 지연시키고 대신 이전의 값을 보여준다.
isPending을 사용하여 업데이트가 지연되는 동안 스피너를 보여줄 수도 있다.
useDeferredValue훅 또한 useTransition훅과 유사하게 낮은 우선순위를 지정할 수 있다. 차이점은 useTransition훅은 함수 실행의 우선순위를 지정하는 반면, useDeferredValue훅은 값의 업데이트 우선순위를 지정한다. 우선순위가 높은 작업을 실행하는 동안 useMemo와 유사하게 이전 값을 계속 들고 있으면서 업데이트를 지연시킨다.
이 훅은 useMemo와 함께 사용하면 더 효과가 좋다. 종속된 값들을 memoization 시키면 불필요한 리렌더링(re-render)을 막으면서 하위 컴포넌트나 상태의 업데이트를 지연시킬 수 있다.
아래는 검색 자동완성을 처리하는 코드다. 사용자 입력 query의 업데이트에 대해 추천 검색어인 suggestions를 띄워 준다. 추천 검색어의 업데이트는 우선순위가 높은 작업이 없을 때만 실행된다.
function Typeahead() {
const query = useSearchQuery('');
const deferredQuery = useDeferredValue(query);
// Memoizing tells React to only re-render when deferredQuery changes,
// not when query changes.
const suggestions = useMemo(() =>
<SearchSuggestions query={deferredQuery} />,
[deferredQuery]
);
return (
<>
<SearchInput query={query} />
<Suspense fallback="Loading results...">
{suggestions}
</Suspense>
</>
);
}
useInsertionEffect는 useLayoutEffect가 동작하기 전에 스타일을 먼저 조작하게 해주는 훅으로 CSS-in-JS 라이브러리가 렌더링도중에 스타일을 삽입할 때 발생하는 성능 문제를 해결한다.
useInsertionEffect의 시그니처는 useEffect와 동일하다. 그러나 모든 DOM 조작 전에 동기적으로 실행된다. useLayoutEffect에서 레이아웃을 읽기 전에 <style>
또는 SVG <defs>
와 같은 전역 DOM 노드를 삽입하는 데 사용해야 한다. 실제로 이러한 CSS 라이브러리 이외의 다른 용도로 사용되는 것은 아니다.
function useCSS(rule) {
useInsertionEffect(() => {
if (!isInserted.has(rule)) {
isInserted.add(rule);
document.head.appendChild(getStyleForRule(rule));
}
});
return rule;
}
function App() {
let className = useCSS(rule);
return <div className={className} />;
}