Programming/React

React Router 예제(함수 방식)

2swan 2023. 9. 12. 09:16

App.js

import {BrowserRouter, Route, Routes} from "react-router-dom"
import './App.css';
import Home from './routers/Home';
import About from "./routers/About";
import Navigation from "./components/Navigation";
import Poster from "./routers/Poster";
import Detail from "./routers/Detail";

function App() {
  return (
    <BrowserRouter>
    <Navigation/>
    <Routes>
      <Route path={"/"} element={<Home/>}></Route>
      <Route path={"/about"} element={<About/>}></Route>
      <Route path={"/poster"} element={<Poster/>}></Route>
      <Route path={"/detail"} element={<Detail/>}></Route>
    </Routes>

    </BrowserRouter>
  );
}

export default App

 

App.css

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
    'Open Sans', 'Helvetica Neue', sans-serif;
  background-color: #eff3f7;
  height: 100%;
}

 

Movie.css

// components 폴더
.movies .movie {
    background-color: white;
    margin-bottom: 70px;
    font-weight: 300;
    padding: 20px;
    border-radius: 5px;
    color: #adaeb9;
    box-shadow: 0 13px 27px -5px rgba(50, 50, 93, 0.25), 0 8px 16px -8px rgba(0, 0, 0, 0.3),
      0 -6px 16px -6px rgba(0, 0, 0, 0.025);
  }
  
  .movies .movie a {
    display: grid;
    grid-template-columns: minmax(150px, 1fr) 2fr;
    grid-gap: 20px;
    text-decoration: none;
    color: inherit;
  }
  
  .movie img {
    position: relative;
    top: -50px;
    max-width: 150px;
    width: 100%;
    margin-right: 30px;
    box-shadow: 0 30px 60px -12px rgba(50, 50, 93, 0.25), 0 18px 36px -18px rgba(0, 0, 0, 0.3),
      0 -12px 36px -8px rgba(0, 0, 0, 0.025);
  }
  
  .movie .movie__title,
  .movie .movie__year {
    margin: 0;
    font-weight: 300;
  }
  
  .movie .movie__title {
    margin-bottom: 5px;
    font-size: 24px;
    color: #2c2c2c;
  }
  
  .movie .movie__genres {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    margin: 5px 0px;
  }
  
  .movie__genres li,
  .movie .movie__year {
    margin-right: 10px;
    font-size: 14px;
  }

 

Movie.js

// components 폴더
import { Link } from 'react-router-dom';
import './Movie.css';

const Movie = ({title, year, summary, poster, genres}) => {
    return(
        <div className="movie">
            <img src={poster} alt={title} title={title}/>
            <div className="movie_data">
                <h3 className="movie_title">app06 {title}</h3>
                <h5 className="movie_year">{year}</h5>
                <ul className="moive_genres">
                    {
                        genres.map((genre,index)=>{
                            return(
                                <li key={index}>
                                    {genre}
                                </li>
                            )
                        })
                    }
                </ul>

                <p>{summary.slice(0,100)}...</p>
                <Link to={'/detail'}
                    state={{year, title, summary, poster, genres}}>
                    Detail
                </Link>
                </div>
                
        </div>
    )
}
export default Movie;

 

Navigation.css

// components 폴더
.nav {
  z-index: 1;
  position: fixed;
  top: 50px;
  left: 10px;
  display: flex;
  flex-direction: column;
  background-color: white;
  padding: 10px 20px;
  box-shadow: 0 13px 27px -5px rgba(50, 50, 93, 0.25), 0 8px 16px -8px rgba(0, 0, 0, 0.3),
    0 -6px 16px -6px rgba(0, 0, 0, 0.025);
  border-radius: 5px;
}

@media screen and (max-width: 1090px) {
  .nav {
    left: initial;
    top: initial;
    bottom: 0px;
    width: 100%;
  }
}

.nav a {
  text-decoration: none;
  color: #0008fc;
  text-transform: uppercase;
  font-size: 12px;
  text-align: center;
  font-weight: 600;
}

.nav a:not(:last-child) {
  margin-bottom: 20px;
}

 


Navigation.js

// components 폴더
import {Link} from "react-router-dom";
import "./Navigation.css"

function Navigation(){
    return(
        <div className="nav">
            <Link to="/">Home</Link>
            <Link to="/about">About</Link>
            <Link to="/poster">Poster</Link>
            
        </div>
    )
}
export default Navigation;

 

About.css

// routers 폴더
.about__container {
  box-shadow: 0 13px 27px -5px rgba(50, 50, 93, 0.25), 0 8px 16px -8px rgba(0, 0, 0, 0.3),
    0 -6px 16px -6px rgba(0, 0, 0, 0.025);
  padding: 20px;
  border-radius: 5px;
  background-color: white;
  margin: 0 auto;
  margin-top: 100px;
  width: 100%;
  max-width: 400px;
  font-weight: 300;
}

.about__container span:first-child {
  font-size: 20px;
}
.about__container span:last-child {
  display: block;
  margin-top: 10px;
}

 

Aboout.js

// routers 폴더
import './About.css'

const About = ()=>{
    return(
        <div className="about_container">
            <span>app06_About</span><br/>
            <span>2023</span>
        </div>
    )
}
export default About

 

Detail.js

// routers 폴더
import { useLocation } from 'react-router-dom'
import './MovieView.css'

const Detail=()=>{
    const location = useLocation();
    console.log({location})
    return(
        <div className='movie_container'>
            <img src={location.state.poster}
            alt={location.state.title} title={location.state.title}/>
            <div className='movie_data'>
                <h3 className='movie_title'>app06 {location.state.title}</h3>
                <h5 className='movie_year'>{location.state.year}</h5>
                <ul className='movie_genres'>
                    {
                        location.state.genres.map((genre, index)=>{
                            return(
                                <li key={index}>
                                    {genre}
                                </li>
                            )
                        })
                    }
                </ul>

            </div>
        </div>
    )
}
export default Detail;

 

Home.css

// routers 폴더
.container {
  height: 100%;
  display: flex;
  justify-content: center;
}

.loader {
  width: 100%;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  font-weight: 300;
}
.movies {
  display: grid;
  grid-template-columns: repeat(2, minmax(400px, 1fr));
  grid-gap: 100px;
  padding: 50px;
  width: 80%;
  padding-top: 70px;
}

@media screen and (max-width: 1090px) {
  .movies {
    grid-template-columns: 1fr;
    width: 100%;
  }
}

 

Home.js

// routers 폴더
import React, { useEffect, useState} from 'react';
import axios from 'axios';
import './Home.css'
import Movie from '../components/Movie';

const Home = ()=>{
    const [movies, setMovies] = useState([])
    const [isLoading, setLoading] = useState(true)
    const getMoives =()=>{
        axios.get('https://yts.mx/api/v2/list_movies.json?sort_by=rating')
        .then((res)=>{
            console.log("res.data:", res.data)
            setMovies(res.data.data.movies)
            setLoading(false)
        })
    }
    //componentDidMount, componentDidUpdate
    useEffect(()=>{
        getMoives()
    },[])
    return(
        <section className='container'>
        {
            isLoading ? (
                <div className='loader'>
                    <span>isLoading</span>
                </div>
            ) : (
                <div className='movies'>
                    {
                        movies.map((movie)=>{
                            return(
                                //사진, title, year, genres, summary
                                <Movie key={movie.id}
                                year = {movie.year}
                                title = {movie.title}
                                summary = {movie.summary}
                                poster = {movie.medium_cover_image}
                                genres = {movie.genres}
                                />
                            )
                        })
                    }
                </div>
            )
        }

    </section>
    )
    
}
export default Home;

 

MovieView.css

// routers 폴더
.movie__container {
  box-shadow: 0 13px 27px -5px rgba(50, 50, 93, 0.25), 0 8px 16px -8px rgba(0, 0, 0, 0.3),
    0 -6px 16px -6px rgba(0, 0, 0, 0.025);
  padding: 20px;
  border-radius: 5px;
  background-color: white;
  margin: 0 auto;
  margin-top: 100px;
  width: 100%;
  max-width: 400px;
  font-weight: 300;
}

.movie__container span:first-child {
  font-size: 20px;
}

.movie__container span:last-child {
  display: block;
  margin-top: 10px;
}

.movie img {
  position: relative;
  top: -50px;
  max-width: 150px;
  width: 100%;
  margin-right: 30px;
  box-shadow: 0 30px 60px -12px rgba(50, 50, 93, 0.25), 0 18px 36px -18px rgba(0, 0, 0, 0.3),
    0 -12px 36px -8px rgba(0, 0, 0, 0.025);
}

.movie .movie__title,
.movie .movie__year {
  margin: 0;
  font-weight: 300;
}

.movie .movie__title {
  margin-bottom: 5px;
  font-size: 24px;
  color: #2c2c2c;
}

.movie .movie__genres {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  margin: 5px 0px;
}

.movie__genres li,
.movie .movie__year {
  margin-right: 10px;
  font-size: 14px;
}

 

Navigation.css

// routers 폴더
.nav {
  z-index: 1;
  position: fixed;
  top: 50px;
  left: 10px;
  display: flex;
  flex-direction: column;
  background-color: white;
  padding: 10px 20px;
  box-shadow: 0 13px 27px -5px rgba(50, 50, 93, 0.25), 0 8px 16px -8px rgba(0, 0, 0, 0.3),
    0 -6px 16px -6px rgba(0, 0, 0, 0.025);
  border-radius: 5px;
}

@media screen and (max-width: 1090px) {
  .nav {
    left: initial;
    top: initial;
    bottom: 0px;
    width: 100%;
  }
}

.nav a {
  text-decoration: none;
  color: #0008fc;
  text-transform: uppercase;
  font-size: 12px;
  text-align: center;
  font-weight: 600;
}

.nav a:not(:last-child) {
  margin-bottom: 20px;
}

 

Poster.js

// routers 폴더
import axios from "axios";
import { useEffect, useState } from "react";
import './Home.css';

const Poster = () => {
    const [movies, setMovies] = useState([]);
    const [isLoading, setLoading] = useState(true);

    const getMovies = () => {
        axios.get('https://yts.mx/api/v2/list_movies.json?sort_by=rating')
        .then((res) => {
            console.log("res.data:", res.data);
            setMovies(res.data.data.movies);
            setLoading(false);
        })
    }

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

    return (
        <section className="container">
            {
                isLoading ? (
                    <div className="loader">
                        <span>Loading</span>
                    </div>
            ) : (
                    <div className="movies">
                    {
                        movies.map((movie) => (
                        <img key={movie.id}
                            src={movie.medium_cover_image}
                            alt={movie.title} 
                        
                        />
                        
                    ))}
                </div>
            )}
        </section>
    );
}

export default Poster;