본문 바로가기

FrontEnd

리액트 라우터 v6를 이용해 쉽게 모달 만들기

반응형

원문 : https://dev.to/devmdmamun/create-contextual-modal-navigation-with-react-router-v6-28k2

 

Create contextual modal navigation with React Router V6.

I'm currently (April 2022) creating a side-project using ReactJs. I have taken inspiration from...

dev.to

리액트 라우터를 이용하면 뒤 배경이 살아있지만, URL이 변경되는 모달을 만들 수 있습니다.

리액트 라우터 v6에 대한 자세한 내용은 해당 게시물을 참고하세요.

 

리액트 라우터 v6(React Router v6) 딥 다이브

https://reactrouter.com/docs/en/v6/getting-started/concepts#main-concepts React Router | Main Concepts Declarative routing for React apps at any scale reactrouter.com 요즘은 대부분의 회사들이 CRA..

itchallenger.tistory.com


Main.js

state props으로 location 객체를 전달합니다.

이에 대해 나중에 더 설명하겠습니다.

import { Link, useLocation } from "react-router-dom";

export const Main = () => {
  const location = useLocation();
  return (
    <div>
      <h2>Create contextual modal navigation</h2>
      {/* <Link /> 요소에는 state 속성이 있습니다. 그것은 개체를 포함합니다. background를 키로 전달하고 location을 값으로 전달합니다. */}
      <Link to="/modal" state={{ background: location }}>
        Open Modal
      </Link>
    </div>
  );
};

App.css

스타일은 생략. 아래 코드 샌드박스나 원문을 참고하세요


Modal.js

주인공이지만 특별한 건 없습니다.

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

export const Modal = () => {
  return (
    <div className="modalDiv">
      <div className="modal">
        <h3>Modal</h3>
        <Link to="/">
          <button>Close</button>
        </Link>
      </div>
    </div>
  );
};

Index.js

<App />을 Index.js 파일 내부에서 <Router />로 래핑했습니다.
App.js 파일에서 React Router가 사용하는 useLocation 훅을를 사용할 것이기 때문입니다.
리액트 라우터가 사용하는 훅을를 <Router /> 외부에 배치할 수 없습니다.
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { BrowserRouter as Router } from "react-router-dom";

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

App.js

열린 모달을 클릭하여 모달을 열 때 ,배경에 빈 페이지가 있는 모달이 아니라,

이전 페이지 상단에 모달을 표시할 것입니다.

따라서 현재 위치 개체를 사용하는 대신 이전 위치 개체를 <Routes />에 전달합니다.

그러면 <Routes />우리가 같은 페이지(이전 위치)에 있다고 생각합니다.

즉, 링크를 클릭하여 모달을 열면 url이 https://localhost:3000/modal로 변경되지만

<Routes />는 위치가 변경되지 않은 것으로 간주합니다.

우리는 main.js 파일에서 state props으로 해당 객체를 전달했습니다.

import "./styles.css";
import { Route, Routes, useLocation } from "react-router-dom";
import { Main } from "./Main";
import { Modal } from "./Modal";
function App() {
  const location = useLocation();
  const background = location.state && location.state.background;

  return (
    <div className="App">
      {/* 백그라운드 객체가 없어도 렌더링 */}
      <Routes location={!background || location}>
        <Route path="/" element={<Main />}>
          {background && <Route path="modal" element={<Modal />} />}
        </Route>
      </Routes>
    </div>
  );
}
export default App;

위 코드에 약간의 트릭이 있습니다.

주 : 원문 코드에 오류가 있어서 이건 해당 코드를 참조해야 합니다.

background 객체가 있는 경우

링크를 클릭하여 모달을 열면 조건부로 두 번째 <Routes /> 컨테이너에 모델이 표시되고

첫 번째 <Routes /> 컨테이너에 의해 배경으로 홈페이지가 표시됩니다.

하지만 모달 페이지를 직접 방문하면 첫 번째 컨테이너에 모달 경로를 추가해도 홈페이지만 보입니다.

 

Main.js 파일에 <Outlet /> 요소를 추가하기만 하면 /modal 경로에 대한 모달 또는 기타 컴포넌트를 표시할 수 있습니다.

import { Link, Outlet, useLocation } from "react-router-dom";
export const Main = () => {
  const location = useLocation();
  return (
    <div>
      <h2>Create contextual modal navigation</h2>
      {/* <Link /> 요소에는 state 속성이 있습니다. 그것은 개체를 포함합니다. background를 키로 전달하고 location을 값으로 전달합니다. */}
      <Link to="/modal" state={{ background: location }}>
        here <Outlet />
      </Link>
    </div>
  );
};

응용

해당 방법을 이용하면, 링크로 접근하면 페이지에 화면을 보여주고, 페이지에서 클릭하면 모달로 보여주는

인스타그램, 핀터레스트와 같은 동작을 구현할 수 있습니다.

같은게 있으면 다음에 나오는게 우선하는 모양입니다.

코드는 여기 : https://stackblitz.com/github/remix-run/react-router/tree/main/examples/modal?file=src%2FApp.tsx 

      <Routes location={state?.backgroundLocation || location}>
        <Route path="/" element={<Layout />}>
          <Route index element={<Home />} />
          <Route path="gallery" element={<Gallery />} />
          <Route path="/img/:id" element={<ImageView />} />
          <Route path="*" element={<NoMatch />} />
        </Route>
      </Routes>

      {/* Show the modal when a `backgroundLocation` is set */}
      {state?.backgroundLocation && (
        <Routes>
          <Route path="/img/:id" element={<Modal />} />
        </Routes>
      )}

 

동작하는 예제는 https://mklikgbza--github--3000.local.webcontainer.io/ 에서 보실 수 있습니다.

 

반응형