React Router의 v6 업데이트가 힘들다고 합니다.

20초 개요


사용 중인 OSS를 업데이트하는 동안 React Router v5에서 v6 업데이트까지는 다소 힘들었습니다.

참고 자료

  • 스태프 블로그
  • (공식) UPGRANDING TO V6-Upgrading fromv5
  • 수정점 잡기

  • <Switch>의 경로 분기는 <Routes>입니다.
  • <Route>의 기본값은 완전히 일치하기 때문에exact가 필요하지 않습니다.
  • <Switch> 내의 <Route> 순서에 주의할 필요가 없습니다.
  • /a에서 지정한 경우 /a/b와 같은 경로가 먼저 일치하지 않습니다.
  • <Route>의 매개 변수가 바뀌었습니다. element에서 이동할 구성 요소를 지정했습니다.
  • <Redirect><Navigate>로 바뀌었다.
  • 이로써 기본 변환도push 대신replace로 대체됩니다.
  • useHistoryuseNavigate로 바뀌었다.
  • 이것은 <Redirect>와 마찬가지로 기본적으로push를 나타낸다.
  • useRouteMatchuseMatch로 바뀌었다.
  • 보조 Route로 등록할 때 절대 경로를 사용하지 않고 상위 Route의 상대 경로로 기재한다.
  • 무슨 곤란한 일이 있습니까?


    후방 교환 없이 파괴적인 업데이트가 진행되었다.
    상기 수정점에 대해서는 기계적으로 교체하면 되지만 참을 수 없는 점을 기계적으로 수정해 설명한다.

    주의

  • React Router v6는 React16입니다.이용 후
  • 딱한 곳


    Routes의 직선 하단 구성 요소는 Route만 허용


    v5 설치


    지금까지 로그인 상황에 따라 로그인 화면으로 강제로 돌아가기 위해<Route> 구성 요소 wrap<PrivateRoute>을 제작했다.
    내용의 처리는 외부 부품과 자체 제작 갈고리를 이용하기 때문에 분위기만 전달하면 좋겠다고 생각합니다.
  • 루트 구성 요소
  • index.tsx
    <Switch>
      <Route path="/login">
        <Login />
      </Route>
      <PrivateRoute path="/menu">
        <Menu />
      </PrivateRoute>
    </Switch>
    
  • PrevateRoute 구성 요소
  • index.tsx
    export const PrivateRoute: React.FC<RouteProps> = ({
      children,
      ...props
    }): JSX.Element => {
      // 自作のuseLoginによるログイン処理の返却値を見て、ログイン状態を確認
      // stateはグローバル状態としてユーザのログイン状態を管理
      const { state } = useLogin();
      // ログイン未済はログイン画面に遷移
      return !state.authenticationStatus ? (
        <Route {...props}>
          <Login />
        </Route>
      ) : (
        // ログイン状態が問題無ければ画面遷移
        <Route {...props}>
          {children}
        </Route>
      );
    };
    

    v6에 대한 기계 수정


    우선 기계적으로 <Switch><Route>으로, 변환 어셈블리도 element 지정으로 변경해 다음과 같은 오류가 발생했다.
  • 루트 구성 요소
  • index.tsx
    <Routes>
      <Route path="/login" element={<Login />} />
      <PrivateRoute path="/menu" element={<Menu />} />
    </Routes>
    
  • 오류 발생
  • router.tss:5 Uncaught Error: [PrivateRoute] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>
        at invariant (router.tss:5:20)
        at components.tsxx:291:5
        at react.development.jss:1104:1
        at react.development.jss:1067:1
        at mapIntoArray (react.development.jss:964:1)
        at mapIntoArray (react.development.jss:1004:1)
        at mapChildren (react.development.jss:1066:1)
        at Object.forEachChildren [as forEach] (react.development.jss:1103:1)
        at createRoutesFromChildren (components.tsxx:275:3)
        at Routes (components.tsxx:256:20)
    

    v6의 공식 대응


    마지막으로 다음과 같은 번잡한 문법이 되었다.
  • 루트 구성 요소
  • index.tsx
    <Routes>
      <Route path="/login" element={<Login />} />
      <Route
        path={screenLocations[MENU_FC]}
        element={
          <PrivateRoute path={screenLocations[MENU_FC]}>
            <Menu />
          </PrivateRoute>
        }
      />
    </Routes>
    

    액세스 경로를 통해 Login 화면으로 리디렉션


    v5 설치


    지금까지 스위치의 어디도 일치하지 않는 상황에서 최종적으로/login Redirect를 진행했다.이 URL 자체도 /가 아닌/login이기 때문에 Redirect를 이용한다.
  • 루트 구성 요소
  • index.tsx
    <Switch>
      <Route path="/login">
        <Login />
      </Route>
      <Route path="/menu">
        <Menu />
      </Route>
      <Redirect to="/login" />
    </Switch>
    

    v6에 대한 기계 수정

    <Redirect><Navigate>이기 때문에 다음과 같이 수정하면 이전 절과 같고<Routes>의 부하<Route>를 오류 출력으로 한다.(replace,push를 의식하지 못함)
    idnex.tsx
    <Routes>
      <Route path="/login">
        <Login />
      </Route>
      <Route path="/menu">
        <Menu />
      </Route>
      <Navigate to="/login" />
    </Routes>
    
  • 오류 로그
  • router.tss:5 Uncaught Error: [Navigate] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>
        at invariant (router.tss:5:20)
        at components.tsxx:291:5
        at react.development.jss:1104:1
        at react.development.jss:1067:1
        at mapIntoArray (react.development.jss:964:1)
        at mapIntoArray (react.development.jss:1004:1)
        at mapChildren (react.development.jss:1066:1)
        at Object.forEachChildren [as forEach] (react.development.jss:1103:1)
        at createRoutesFromChildren (components.tsxx:275:3)
        at Routes (components.tsxx:256:20)
    

    v6의 공식 대응


    마지막으로 우리는 다음과 같은 문법을 채택했다.
    idnex.tsx
    <Routes>
      <Route path="/login" element={<Login />} />
      <Route path="/menu" element={<Menu />} />
      <Route
        path="/"
        element={<Navigate to={screenLocations[LOGIN_FC]} />}
      />
    </Routes>
    

    Routes 중첩 시 하위 Route를 와일드카드로 등록해야 함


    v5 설치

  • 루트 어셈블리의 경우 Switch에서 경로 매핑을 통해 서브어셈블리를 등록
  • index.tsx
    <Switch>
      <Route path="/login" exact>
        <Login />
      </Route>
      <Route path="/menu">
        <Menu />
      </Route>
      <Route path="/user">
        <Users />
      </Route>
    </Switch>
    
  • 각 서브어셈블리의 세부 라우팅
  • Users 어셈블리에 UserList 및 UserDetail 어셈블리 표시
  • index.tsx
    <Switch>
      <Route path="/user" exact>
        <UserList />
      </Route>
      <Route path="/user/details">
        <User />
      </Route>
    </Switch>
    

    v6에 대한 기계 수정

  • 루트 어셈블리에서 Switch를 Routes로 변경하고 요소 속성에 컴포넌트를 등록합니다.
  • index.tsx
    <Routes>
      <Route path="/login" element={<Login />} />
      <Route path="/menu" element={<Menu />} />
      <Route path="/user" element={<User />} />>
    </Routes>
    
  • 서브어셈블리도 마찬가지로 Switch를 Routes로 변경하여 요소 속성에 컴포넌트를 등록합니다.이 경우 상위 Route의 상대 경로가 설정됩니다.
  • index.tsx
    <Routes>
      <Route path="/" elements={<UserList />} />
      <Route path="/details" elements={<User />} />
    </Routes>
    
  • 오류 발생
  • VM1618 react_devtools_backend.js:3973 You rendered descendant <Routes> (or called `useRoutes()`) at "/workflow" (under <Route path="/user">) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render.
    
    Please change the parent <Route path="/user"> to <Route path="/user/*">.
    
    v6는 하위 경로를 렌더링할 때 경로의 다음 경로를 와일드카드를 통해 허가해야 한다.
    이는 스위치가 더 이상 애매하지 않고 exact가 폐지된 것과 관련이 있어 정확한 루트를 진행하는 일환이라고 할 수 있다.

    v6의 공식 대응

  • 서브어셈블리에 서브루트가 있는 경우 루트 어셈블리에 와일드카드를 지정합니다.
  • index.tsx
    <Routes>
      <Route path="/login" element={<Login />} />
      <Route path="/menu" element={<Menu />} />
      <Route path="/user/*" element={<User />} />>
    </Routes>
    
  • 서브어셈블리는 변경되지 않으며 지정된 서브경로는 와일드카드 이후의 경로를 지정합니다.
  • index.tsx
    <Switch>
      <Route path="/" elements={<UserList />} />
      <Route path="/details" elements={<User />} />
    </Switch>
    

    외국 편


    업데이트에 따라 팩스를 진행하는 과정에서 수정이 발생했습니다. v6와는 상관없지만 조금 헷갈립니다.

    현재 경로의 획득 방법을useRouteMatch에서useLocation으로 변경하면 "/" 가 사라집니다


    원래useLocation에서 pathname을 얻었어야 했는데, 아래의 현재 경로를 얻었습니다.
  • 수정 전
  • index.tsx
    const match = useRouteMatch();
    return <Redirect to={`${match.path}details`} />;
    
    useRouteMatch가useMatch가 된 일도 있다. 현재의 경로를 얻는 목적은 useLocation이나useHistory를 사용하는 것을 다시 변경했다.
    이때, location.pathname의 끝에 /가 없기 때문에 다음과 같이 수정되었습니다.
  • 수정 후
  • index.tsx
    const location = useLocation();
    return <Navigate to={`${location.pathname}/details`} />
    
    ※ v5에서는 useHistory에서 History까지.location.pathname처럼 현재 경로를 얻었습니다.useLoocation과 달리 useLoocation은 렌더링할 때 정음형이고 useHistory할 때 정음형이다.사용 장소를 명확히 구분해야 하는 경우는 드물지만, useHistory가 useNavigate로 바뀌었기 때문에 실제 useLocation에서 현재의 경로를 얻을 수 밖에 없다.

    끝맺다


    왜 이렇게 파괴적인 업데이트가 있었을까? 스태프의 블로그에 언급됐다.
    새 라우터가 발행되는 가장 큰 이유는 리액트 연계의 등장이다.
    클래스에 React 구성 요소를 설치하지 말고 React와 연결된 함수 구성 요소를 이용하여 구성 요소의 흐름을 만듭니다.
    리액트18에서도 화제<React.Suspense>React.lazy()의 친화력을 다뤘고샘플 코드도 제공했다.
    또 v5와의 후방 교환도 개발 중이다.

    좋은 웹페이지 즐겨찾기