[React 6강] Router

youngseo·2022년 8월 10일
0

REACT

목록 보기
41/52
post-thumbnail

Router

최근 react-router-dom 버전 6로 업그레이드 되었습니다. 버전 6과 버전 5는 많은 차이가 있기 때문에 그부분에 있어서도 중점적으로 확인해보는 것을 권장드립니다.

1. react-router-dom@6설치

$ yarn add react-router-dom@6

2. BrowserRouter

index.js

import { BrowserRouter } from "react-router-dom";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

3. 폴더 구조 세팅

  • 공식문서에서는 routes 폴더를 만들어 그 안에서 컴포넌트를 만들어 사용하는 것을 권장하고 있습니다.
  • Next.js의 경우 pages폴더 안에서 폴더를 어떻게 만드느냐에 따라서 자동으로 ui를 세팅해주기 때문에 미리 연습해본다는 마음으로 관리를 해보는 것도 좋습니다.
  • src 하위 components폴더 내에 각각 Posts폴더, Users폴더를 생성해 그 하위에 index.js파일을 만들어줍니다.

4. Router 설정

  • 보통 layouts라는 폴더를 만들어 그안에 파일들을 만들어 세팅하기도 하지만 저희는 app.js에 바로 세팅하도록 하겠습니다.

App.js

import React from "react";
import { Routes, Route } from "react-router-dom";
import Posts from "./Posts";
import Users from "./Users";

function App() {
  return (
    <div>
      <Routes>
        <Route path="posts" element={<Posts />} />
        <Route path="users" element={<Users />} />
      </Routes>
    </div>
  );
}

export default App;
  • Route는 어떤 주소로 접근했을 때 어떤 컴포넌트를 보여주고 싶은지를 정하는 태그입니다.
    • <Route path="접속할 port" element={보여줄 컴포넌트} />
  • Routes주소변경을 감지해서 아래에 있는 route중 일치하는 route를 찾고 보여주는 역할을 합니다.

5. Link태그

  • 특정 페이지로 이동하는 버튼을 만들고 싶을 때 사용하는 것이 바로 Link태그입니다.
    • <Link to="posts">Posts</Link> to="" 이동하고 싶은 페이지
    • path="*"을 쓰는 경우 일치하지 않는 모든 경로에 해당하는 경우 해당 컴포넌트를 보여줍니다.
function App() {
  return (
    <div>
      <Link to="posts">Posts</Link> | <Link to="users">Users</Link>
      <Routes>
        <Route path="posts" element={<Posts />} />
        <Route path="users" element={<Users />} />
        <Route path="*" element={<p>Not Found</p>} />
      </Routes>
    </div>
  );
}

6. Nesting Route & Outlet

  • Posts페이지에서 글제목 목록을 보여준 후 각각의 제목을 클릭시 해당하는 postDetail페이지로 이동하는 코드를 작성해보도록 하겠습니다.

6-1 동적 route설정

App.js

<Routes>
  <Route path="posts" element={<Posts />}>
    <Route path=":id" element={<PostDetail />} />
  </Route>
</Routes>

6-2 outlet설정

Post.js outlet

import { Outlet } from "react-router-dom";

function Posts() {
  return (
    <div>
      <Outlet />
    </div>
  );
}

6-3 index Route 설정

  • post페이지로 접속시 보여줄 기본 요소들을 설정해줄 페이지를 세팅해보도록 하겠습니다.
  • 먼저, Posts폴더 내에 PostIndex폴더와 그 하위에 index.js파일을 만들어줍니다.

App.js

<Route path="posts" element={<Posts />}>
  <Route index element={<PostIndex />} /><Route path=":id" element={<PostDetail />} />
</Route>
  • 위와 같이 path경로 없이 index라고만 작성을 한 후 element를 설정해줍니다.
  • 이렇게 설정을 해주면 post페이지로 접속시 postIndex창이 보이고 :id로 이동시 해당 부분에 postDetail이 출력되게 됩니다.

Route구조 정리

  • Posts페이지 : nav바라던지, 뒤로가기 공통요소를 넣습니다.
  • Index페이지 : PostList
  • :id : 디테일페이지

6-4 동적 아이디 받아오기 useParams

PostDetail

import { useParams } from "react-router-dom";

function PostDetail() {
  const params = useParams();
  return <div>{params.id}</div>;
}
  • 주소 posts/:id 의 id값이 페이지에 출력되게 됩니다.

6-5 데이터 연결

Posts>PostIndex>index.js

function PostIndex() {
  return (
    <div>
      {postData.map((post) => (
        <Link to={`/posts/${post.id}`}>
          <p>{post.title}</p>
        </Link>
      ))}
    </div>
  );
}

posts>PostDetail>index.js
useParams로 얻어낸 해당 아이디에 맞는 디테일 정보를 표시합니다.

function PostDetail() {
  const params = useParams();
  const post = postData.find((post) => post.id === parseInt(params.id));
  return (
    <>
      ****
      <div>{post.title}</div>
      <div>{post.body}</div>
    </>
  );
}

단, params.id는 문자, post.id는 숫자이기 때문에 타입을 일치 시켜줘야합니다.

7. 검색기능 추가 SearchParams

7-1 SearchParamst설정

function PostIndex() {
  const [searchParams, setSearchParams] = useSearchParams();
  const [posts, setPosts] = useState(postData);

  useEffect(() => {
    setPosts(
      postData.filter((post) => {
        const filter = searchParams.get("filter");
        const title = post.title.toLowerCase();
        return filter ? title.includes(filter) : true;
      })
    );
  }, []);

  return (
    <div>
      {posts.map((post) => (
        <Link to={`/posts/${post.id}`}>
          <p>{post.title}</p>
        </Link>
      ))}
    </div>
  );
}

7-2 input설정

function PostIndex() {
  const [searchParams, setSearchParams] = useSearchParams();
  const [posts, setPosts] = useState(postData);

  const searchInputHandler = (e) => {
    const filter = e.target.value;
    filter ? setSearchParams({ filter }) : setSearchParams({});
  };

  useEffect(() => {
    setPosts(
      postData.filter((post) => {
        const filter = searchParams.get("filter");
        const title = post.title.toLowerCase();
        return filter ? title.includes(filter) : true;
      })
    );
  }, [searchParams]);

  return (
    <div>
      <input onChange={searchInputHandler} />
      {posts.map((post) => (
        <Link to={`/posts/${post.id}`}>
          <p>{post.title}</p>
        </Link>
      ))}
    </div>
  );
}

8. useLocation의 state활용하기

  • 현재 url관련한 위치를 알려줍니다.

  • 아래와 같이 hash, key, pathname(주소), search, state(주소와 함께 특정정보를 전달할수 있습니다.) 정보가 들어있습니다.

  • 사실 PostDetail 페이지의 경우 단순 id와 일치하는 정보만 필요하기 때문에 postData를 불러올 필요는 없습니다.

  • 즉, const post = postData.find((post) => post.id === parseInt(params.id));부분을 PostIndex페이지에서 내려줘도 괜찮습니다.

PostIndex > index.js

return (
  <div>
    <input onChange={searchInputHandler} />
    {posts.map((post) => (
      <Link
        to={`/posts/${post.id}`}
        state={{
          post: posts.find((data) => data.id === post.id),
        }}>
        <p>{post.title}</p>
      </Link>
    ))}
  </div>
);

PostDetail > index.js

import React from "react";
import { useLocation } from "react-router-dom";

function PostDetail() {
  const location = useLocation();
  //post가 없더라도 에러가 나지 않도록 null처리
  const { post } = location.state ? location.state : { post: null };
  //post가 없는 경우 not found
  if (!post) return <p>Not Found</p>;
  return (
    <>
      <div>title: {post.title}</div>
      <div>body: {post.body}</div>
    </>
  );
}

export default PostDetail;

9. useNavigate

  • 주소이동시 사용을 하는 hook입니다.
  • Link태그와 사실상 역할은 유사하나 navigate의 경우 함수 안에서 사용이 가능하다는 점에서 차이점이 있습니다.
  • Link태그도 state에 대한 정보를 넘길 수 있었듯이 navigate도 state에 대한 정보를 넘길 수 있습니다.
  • onClick={() => navigate(-1, {replace:true})}로 설정하는 경우 기록이 아닌, 상위 루트로 가게 됩니다.
import { useLocation, useNavigate } from "react-router-dom";

function PostDetail() {
  const location = useLocation();
  const navigate = useNavigate();const { post } = location.state ? location.state : { post: null };

  if (!post) return <p>Not Found!</p>;
  return (
    <>
      <div>title: {post.title}</div>
      <div>body: {post.body}</div>
      <button onClick={() => navigate("/users", {state: {data:1}})}>유저로 가기</button><button onClick={() => navigate(-1)}>뒤로가기</button>
    </>
  );
}

App.js

function App() {
  const navigate = useNavigate();
  const location = useLocation();
  return (
    <div>
      <h5>{location.pathname}</h5>
      <button onClick={() => navigate(-1)}>뒤로가기</button>
      <nav>
        <Link to="posts">Posts</Link> | <Link to="users">Users</Link>
      </nav>
      <Routes>
        <Route path="posts" element={<PostIndex />}>
          <Route index element={<Posts />} />
          <Route path=":id" element={<PostDetail />} />
        </Route>
        <Route path="users" element={<Users />}>
          <Route index element={<UserIndex />} />
          <Route path=":id" element={<UserDetail />} />
        </Route>
        <Route path="*" element={<p>Not Found</p>} />
      </Routes>
    </div>
  );
}

PostIndex > index.js

{
  posts.map((post) => (
    <NavLink
      style={({ isActive }) => ({ color: isActive ? "red" : "black" })}
      to={`/posts/${post.id}`}
      state={{
        post: posts.find((data) => data.id === post.id),
      }}
    >
      <p>{post.title}</p>
    </NavLink>
  ));
}

11. Axios활용

App.js

<Route path="albums" element={<Albums />}>
  <Route index element={<AlbumList />} />
</Route>

Albums > index.js

function Albums() {
  return (
    <div>
      <Outlet />
    </div>
  );
}

AlbumList > index.js

function AlbumList() {
  const [albums, setAlbums] = useState();

  const fetchData = async () => {
    const { data } = await axios.get(
      "https://jsonplaceholder.typicode.com/albums"
    );
    setAlbums(data);
  };

  useEffect(() => {
    fetchData();
  }, []);

  return (
    <div>
      {albums &&
        albums.map((album) => (
          // to={`${album.id}`}동일합니다.
          <Link to={`/albums/${album.id}`}>
            <p>{album.title}</p>
          </Link>
        ))}
    </div>
  );
}

0개의 댓글