react-router에는 hook과 동일한 동작을 하는 component들이 존재한다. 예를들어 useNavigate라는 훅이 있으면 Navigate라는 컴포넌트도 정의되어 있는 것이다.

Navigate코드보기


코드를 살펴보면 상당히 간단하게 되어 있는 것을 알 수 있다. invariant와 waring은 에러를 처리하는 부분이니 넘어가면 useNavigate 훅을 받아서 useEffect에서 해당 훅을 실행하는 것을 확인할 수 있다.

아래 useNavigate코드를 보면 알 수 있듯이 NavigateFunction 타입이 To와 NavigationOptions를 받는것을 알 수 있다. 여기서 NavigationOptions가 replace와 state를 옵셔널로 가지는 타입이기 때문에 Navigate컴포넌트에서 replace와 state를 prop으로 받아주는 것을 볼 수 있다.

export function Navigate({ to, replace, state }: NavigateProps): null {
  invariant(
    useInRouterContext(),
    // TODO: This error is probably because they somehow have 2 versions of
    // the router loaded. We can help them understand how to avoid that.
    `<Navigate> may be used only in the context of a <Router> component.`
  );

  warning(
    !React.useContext(NavigationContext).static,
    `<Navigate> must not be used on the initial render in a <StaticRouter>. ` +
      `This is a no-op, but you should modify your code so the <Navigate> is ` +
      `only ever rendered in response to some user interaction or state change.`
  );

  let navigate = useNavigate();
  React.useEffect(() => {
    navigate(to, { replace, state });
  });

  return null;
}

useNavigate 코드보기


export interface NavigateFunction {
  (to: To, options?: NavigateOptions): void;
  (delta: number): void;
}

export interface NavigateOptions {
  replace?: boolean;
  state?: any;
}

export function useNavigate(): NavigateFunction {
  invariant(
    useInRouterContext(),
    // TODO: This error is probably because they somehow have 2 versions of the
    // router loaded. We can help them understand how to avoid that.
    `useNavigate() may be used only in the context of a <Router> component.`
  );

  let { basename, navigator } = React.useContext(NavigationContext);
  let { matches } = React.useContext(RouteContext);
  let { pathname: locationPathname } = useLocation();

  let routePathnamesJson = JSON.stringify(
    matches.map((match) => match.pathnameBase)
  );

  let activeRef = React.useRef(false);
  React.useEffect(() => {
    activeRef.current = true;
  });

  let navigate: NavigateFunction = React.useCallback(
    (to: To | number, options: NavigateOptions = {}) => {
      warning(
        activeRef.current,
        `You should call navigate() in a React.useEffect(), not when ` +
          `your component is first rendered.`
      );

      if (!activeRef.current) return;

      if (typeof to === "number") {
        navigator.go(to);
        return;
      }

      let path = resolveTo(
        to,
        JSON.parse(routePathnamesJson),
        locationPathname
      );

      if (basename !== "/") {
        path.pathname = joinPaths([basename, path.pathname]);
      }

      (!!options.replace ? navigator.replace : navigator.push)(
        path,
        options.state
      );
    },
    [basename, navigator, routePathnamesJson, locationPathname]
  );

  return navigate;
}

이처럼 wrapper component도 결국 hook으로 구현되어 있기 때문에 함수형 컴포넌트 내에서 사용하려면 그냥 hook을 사용하면 될 것 같다. 각각의 상황에 맞게 사용해보자!

reference