react-router v6의 useNavigate 후크가 낭비되는 재렌더링을 트리거하는 이유와 해결 방법
10960 단어 javascriptperformancereactwebdev
import { useNavigate } from "react-router-dom"; // v6
...
const Component = () => {
const navigate = useNavigate();
...
}
구성 요소에서
useNavigate
후크를 사용하면 경로가 변경되지 않은 경우에도 navigate()
를 호출하거나 <Link />
를 클릭할 때마다 다시 렌더링됩니다. React.memo()
로 이를 방지할 수 없습니다.데모는 다음과 같습니다.
첫 번째 블록은 호출하지 않고
useNavigate
한 번만 렌더링됩니다. 두 번째는 후크를 사용하고 모든 경로 "변경"에서 두 번 다시 렌더링됩니다(왜 두 번, 아마도 useNavigate
가 다시 비난을 받는지 명확하지 않습니다 🤷). 세 번째는 useNavigate
의 "안정적인"버전을 사용합니다. 자세한 내용은 아래에서 설명합니다.특히 react-router v5의
useHistory
가 다시 렌더링을 일으키지 않았기 때문에 이것은 예상치 못한 동작이라고 말하고 싶습니다. 이 동작에 대한 GitHub의 긴 글discussion이 있습니다. 그것은 버그가 아니라 예상되는 동작이라는 입장으로 귀결됩니다.에 대한 댓글
#7634
timdorr
에 댓글을 달았습니다.
useNavigate
는 현재 위치가 변경되면 변경됩니다. 상대 탐색에 따라 달라집니다. memo
로 래핑하면 상위 구성 요소에서 다시 렌더링되는 것만 방지됩니다. 구성 요소 내의 후크로 인해 다시 렌더링이 발생하면 아무 것도 할 수 없습니다memo
.
View on GitHub
useNavigate
subscribes to contexts 경로 변경이 트리거될 때 변경되기 때문에 발생합니다(동일하게 유지되더라도).
let { basename, navigator } = React.useContext(NavigationContext);
let { matches } = React.useContext(RouteContext);
let { pathname: locationPathname } = useLocation();
일반적으로 경로 변경은 보기 변경을 의미하고 어쨌든 새 구성 요소 세트를 렌더링해야 하기 때문에 큰 문제는 아닙니다. 여러 메뉴 요소를 다시 렌더링하는 것은 문제가 되지 않습니다.
그러나 뷰를 변경하지 않고 경로에서 매개변수를 변경하거나 경로 변경과 독립적인 상수 구성 요소가 많으면 고통스러울 수 있습니다.
이 문제를 해결하는 방법에는 여러 가지가 있습니다.
let { basename, navigator } = React.useContext(NavigationContext);
let { matches } = React.useContext(RouteContext);
let { pathname: locationPathname } = useLocation();
useNavigate
가능한 가장 작은/최저 수준 구성 요소에 후크를 사용합니다. 다시 렌더링하지 않아도 되지만 비용이 적게 듭니다. navigate
기능을 트리거할 수 있습니다. 팝업 및 알림 구성 요소 자체에 대한 후크를 이동할 수 있지만 그렇지 않으면 간단한 설정이 불필요하게 복잡해집니다. useRef
후크에서 변경 가능한 개체를 활용하여 후크를 "안정화"합니다. 이것은 this approach 의 단순화된 버전입니다.// StableNavigateContext.tsx
import {
createContext,
useContext,
useRef,
MutableRefObject
} from "react";
import {
useNavigate,
NavigateFunction
} from "react-router-dom";
const StableNavigateContext = createContext<MutableRefObject<
NavigateFunction
> | null>(null);
const StableNavigateContextProvider = ({ children }) => {
const navigate = useNavigate();
const navigateRef = useRef(navigate);
return (
<StableNavigateContext.Provider value={navigateRef}>
{children}
</StableNavigateContext.Provider>
);
};
const useStableNavigate = (): NavigateFunction => {
const navigateRef = useContext(StableNavigateContext);
if (navigateRef.current === null)
throw new Error("StableNavigate context is not initialized");
return navigateRef.current;
};
export {
StableNavigateContext,
StableNavigateContextProvider,
useStableNavigate
};
// App.tsx
import { BrowserRouter } from "react-router-dom";
import {
StableNavigateContextProvider
} from "./StableNavigateContext";
export default function App() {
return (
<BrowserRouter>
<StableNavigateContextProvider>
// ...
</StableNavigateContextProvider>
</BrowserRouter>
);
}
// Component file
import { useStableNavigate } from "./StableNavigateContext";
const Component = () => {
const navigate = useStableNavigate();
// ...
};
useLocation
후크에 대해 유사한 접근 방식을 사용하거나 original solution 에서와 같이 하나의 컨텍스트에서 결합할 수 있습니다. 그러나 구성 요소는 더 이상 경로 변경 시 다시 렌더링되지 않으므로 상태가 오래될 수 있습니다.
Reference
이 문제에 관하여(react-router v6의 useNavigate 후크가 낭비되는 재렌더링을 트리거하는 이유와 해결 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/shallowdepth/why-usenavigate-hook-in-react-router-v6-triggers-waste-re-renders-and-how-to-solve-it-5566텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)