React Router: v5에서 업데이트

45888 단어 react
이것은 내 앱의 React Router를 v5에서 v6으로 업데이트했을 때의 메모입니다.
데모 앱은 here 입니다.

스위치 -> 경로



Switch 대신 Routes를 사용하고 v6에서는 정확한 키워드가 필요하지 않습니다. 또한 element는 Route 페이지 내부에 컴포넌트를 할당하는 새로운 속성입니다.

v5



    <Switch>
     <Route path="/" exact>
      <LaunchPage />
     </Route>
     <Layout>
      <Switch>
       <Route path="/search-book">
        <Home />
       </Route>
       <Route path="/my-books">
        <MyBooks />
       </Route>
      </Switch>
     </Layout>
    </Switch>

v6



사소한 발견은 라우트 내부에 레이아웃 구성 요소를 넣는 것이 불가능하다는 것입니다. 따라서 Layout에서 각 페이지 구성 요소를 각각 래핑해야 합니다. 또한 라우터가 경로에 하나 이상의 하위 경로가 있음을 알리려면 "*"가 중요합니다.

    <Routes>
     <Route path="/" element={<LaunchPage />} />
     <Route path="search-book/" element={<Layout><Home /></Layout>} />   
     <Route path="my-books" element={<Layout><MyBooks /></Layout>}/></Routes>


**리팩토링
Routes에서 반복되는 패턴을 피하기 위해 WithLayout 구성 요소와 래핑된 페이지 구성 요소를 추가했습니다.

레이아웃 포함

    import Layout from "../Layout/Layout.jsx";

    function WithLayout(Child) {
      return function WithLayout(props) {
        return (
          <Layout>
            <Child {...props} />
          </Layout>
        );
      };
    }

    export default WithLayout;




    import SearchBooks from "../components/SearchBooks";
    import WithLayout from "../hoc/WithLayout";

    const Home = () => {
      return <SearchBooks />;
    };
    export default **WithLayout(Home)**;


내 책들

    import { Fragment, useContext } from "react";
    import MyBookItem from "../components/MyBookItem";
    import SortMyBooks from "../components/SortMyBooks";
    import MyBooksContext from "../store/my-books-context";
    import classes from "./MyBooks.module.css";
    import WithLayout from "../hoc/WithLayout";

    // functions

    const MyBooks = () => {
      // functions

    return (
        <Fragment>
          <SortMyBooks
            onTitle={sortByTitleHandler}
            onDate={sortByDateHandler}
            onRating={sortByRatingHandler}
            onDefault={defaultHandler}
          />
          <div className={classes.box}>
            {myBooksCtx.myBooks.length !== 0 && content}
            {myBooksCtx.myBooks.length === 0 && (
              <p>No books here yet. Let's add your book!</p>
            )}
          </div>
        </Fragment>
      );
    };
    export default **WithLayout(MyBooks)**;


찾을 수 없음

    import React from "react";
    import { Link } from "react-router-dom";
    import classes from "./NotFound.module.css";
    import WithLayout from "../hoc/WithLayout";

    function NotFound() {
      return (
        <div className={classes["not-found"]}>
          <h1>404</h1>
          <h2>Page Not Found </h2>
          <p>
            Back to <Link to="/search-book">Home</Link>
          </p>
        </div>
      );
    }

    export default WithLayout(NotFound);




    import React, { Fragment, Suspense } from "react";
    import { Route, Routes } from "react-router-dom";
    import LaunchPage from "./pages/LaunchPage";
    import Home from "./pages/Home";
    import LoadingSpinner from "./UI/LoadingSpinner";
    import NotFound from "./pages/NotFound";
    const MyBooks = React.lazy(() => import("./pages/MyBooks"));

    function App() {
      return (
        <Fragment>
          <Suspense
            fallback={
              <div className="centered">
                <LoadingSpinner />
              </div>
            }
          >
            <Routes>
              <Route path="/" element={<LaunchPage />} />
              <Route path="search-book/*" element={<Home />} />
              <Route path="my-books" element={<MyBooks />} />
              <Route path="*" element={<NotFound />} />
            </Routes>
          </Suspense>
        </Fragment>
      );
    }

    export default App;


전체 경로 -> 상대 경로



하위 경로를 추가할 때 상대 경로를 사용하는 반면 v5에서는 전체 경로를 사용해야 했습니다.

v5




    <Route path={match.url} exact>
     //*elements*
    </Route>
    <Route path={`${match.url}/add-my-books`}>
      <AddMyBooks />
    </Route>


v6




    <Routes>
     <Route path="" element={
      <BookDetailComponent path={location.pathname} />
      }/>
     <Route path="add-my-books" element={<AddMyBooks />} />
    </Routes>


useRouteMatch -> useLocation



공식 문서에 따르면 useMatch는 useRouteMatch의 대안입니다. 그러나 useRouteMatch를 사용하는 방식에 따라 useLocation이 대안이 될 수 있습니다. 필자의 경우 이전 경로를 알기 위해 useRouteMatch를 사용했는데 useLocation이 대체 솔루션이 된 경우입니다.

v5




    const BookItem = (props) => {
      const itemCtx = useContext(ItemContext);
      const match = useRouteMatch();
      const updateItemHandler = () => {
        itemCtx.updateItem(props.item);
      };
      return (
        <Fragment>
          <Link
            className={classes.box}
            to={`${match.path}/${props.item.id}`}
            onClick={updateItemHandler}
          >
            <img
              className={classes.image}
              src={props.item.image ? props.item.image : noImage}
              alt={props.item.title}
            ></img>
          </Link>
        </Fragment>
      );
    };


v6




    const BookItem = (props) => {
      const itemCtx = useContext(ItemContext);
      const location = useLocation();
      const updateItemHandler = () => {
        itemCtx.updateItem(props.item);
      };
      return (
        <Fragment>
          <Link
            className={classes.box}
            to={`${location.pathname}/${props.item.id}`}
            onClick={updateItemHandler}
          >
            <img
              className={classes.image}
              src={props.item.image ? props.item.image : noImage}
              alt={props.item.title}
            ></img>
          </Link>
        </Fragment>
      );
    };


useHistory -> useNavigate



useHistory는 더 이상 사용할 수 없으며 이전 페이지에서 다른 페이지로 이동하려는 경우 대신 useNavigate를 사용하십시오.

v5




    const history = useHistory();
    const submitHandler = (event) => {
        event.preventDefault();
        const formattedDate = formatDate(inputDate);
        const mybook = {
          id: "" + inputDate.getTime() + itemCtx.item.id,
          date: formattedDate,
          rating,
          title: itemCtx.item.title,
          authors: itemCtx.item.authors,
          image: itemCtx.item.image,
          comment,
        };

    myBooksCtx.updateMyBooks(mybook);
        history.replace("/my-books");
      };


v6



교체할 때 지정할 개체를 넣습니다.

    const navigate = useNavigate();
    const submitHandler = (event) => {
        event.preventDefault();
        const formattedDate = formatDate(inputDate);
        const mybook = {
          id: "" + inputDate.getTime() + itemCtx.item.id,
          date: formattedDate,
          rating,
          title: itemCtx.item.title,
          authors: itemCtx.item.authors,
          image: itemCtx.item.image,
          comment,
        };

    myBooksCtx.updateMyBooks(mybook);
        navigate("/my-books", { replace: true });
      };


활성 클래스 이름 -> isActive



activeClassName이 삭제되었고 대신 isActive를 사용하여 특정 탐색이 활성화되었는지 여부를 인식해야 합니다.

v5




    const MainNavigation = () => {
      return (
        <header className={classes.header}>
          <div className={classes.logo}>Your Library</div>
          <nav className={classes.nav}>
            <ul>
              <li>
                <NavLink to="/search-book" exact activeClassName={classes.active}>
                  Home
                </NavLink>
              </li>
              <li>
                <NavLink to="/my-books" activeClassName={classes.active}>
                  My Books
                </NavLink>
              </li>
              <li>
                <NavLink to="/" exact activeClassName={classes.active}>
                  Logout
                </NavLink>
              </li>
            </ul>
          </nav>
        </header>
      );
    }


v6




    const MainNavigation = () => {
      return (
        <header className={classes.header}>
          <div className={classes.logo}>Your Library</div>
          <nav className={classes.nav}>
            <ul>
              <li>
                <NavLink
                  to="/search-book"
                  className={({ isActive }) => (isActive ? classes.active : null)}
                >
                  Home
                </NavLink>
              </li>
              <li>
                <NavLink
                  to="/my-books"
                  className={({ isActive }) => (isActive ? classes.active : null)}
                >
                  My Books
                </NavLink>
              </li>
              <li>
                <NavLink
                  to="/"
                  className={({ isActive }) => (isActive ? classes.active : null)}
                >
                  Logout
                </NavLink>
              </li>
            </ul>
          </nav>
        </header>
      );
    };


리디렉션 -> 탐색



Redirect를 사용하지는 않았지만, Redirect의 삭제는 v5에서 또 다른 큰 변화라고 생각합니다. 대체 구성 요소는 탐색이지만 속성이 약간 변경되었습니다. 리디렉션에서는 교체가 기본값이었고 필요한 경우 푸시를 사용했습니다. 반면에 탐색에서는 푸시가 기본값이며 바꾸기를 사용해야 합니다.

v5




    <Redirect to="contactme" />
    <Redirect to="home" push />


v6




    <Navigate to="contactme" replace />
    <Navigate to="home" />


the official document 에서 더 자세한 정보를 찾을 수 있습니다.

이 업데이트에 대한 나의 인상은 v6에서 중첩 경로를 처리하기가 더 쉬워진다는 것입니다. 또한 요소를 사용하기 때문에 페이지 구성 요소와 기타 구성 요소를 분리하는 것이 더 중요해집니다. 그러나 activeClassName을 삭제하는 것은 나에게 약간 불편합니다.

읽어 주셔서 감사합니다 :)

원문은 here

좋은 웹페이지 즐겨찾기