YeonFlix day 02

thisisyjin·2022년 5월 12일
0

Dev Log 🐥

목록 보기
6/23

Day 02 - 220512

1. 세부 컴포넌트 작성

  • MovieDetail
  • MovieGroup
  • MovieSearch
  • MovieSlide

2. router 작성

  • Home

📝 기획안 보기
🎨 디자인 보기


컴포넌트 작성

MovieDetail

prop-types 설치

$ yarn add prop-types

MovieDetail.js

import PropTypes from 'prop-types';

const MovieDetail = ({
  coverImg,
  rating,
  runtime,
  description_full,
  title,
  genres,
}) => {
  return (
    <div className="movie">
      <div className="shortview">
        <div className="shortview-img">
          <img src={coverImg} alt={title} />
        </div>
        <div className="shortview-text">
          <h3>{title}</h3>
          {rating && <p>{`rating: ${rating} / 10`}</p>}
          {runtime && <p>{`runtime: ${runtime} min`}</p>}
          {genres && (
            <div>
              <h4>genres</h4>
              <ul>
                {genres.map((g) => (
                  <li key={g}>{g}</li>
                ))}
              </ul>
            </div>
          )}
          {description_full && (
            <div className="desc">
              <p>{description_full}</p>
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

MovieDetail.propTypes = {
  rating: PropTypes.number,
  runtime: PropTypes.number,
  coverImg: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired,
  description_full: PropTypes.string,
  genres: PropTypes.arrayOf(PropTypes.string).isRequired,
};

export default MovieDetail;

MovieGroup

import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';

const MovieGroup = ({
  id,
  coverImg,
  title,
  rating,
  runtime,
  year,
  summary,
}) => {
  return (
    <div>
      <div className="shortview">
        <div className="shortview-img">
          <img src={coverImg} alt={title} />
        </div>
        <div className="shortview-text">
          <div className="title">
            <h3>
              <Link to={`/movie/${id}`}>
                {title.length > 35 ? `${title.slice(0, 35)}...` : title}
              </Link>
            </h3>
          </div>
          <div className="info">
            <p>{year && `year: ${year}`}</p>
            <p>{rating && `rating: ${rating} / 10`}</p>
            <p>{runtime && `runtime: ${runtime} min`}</p>
            <p>
              {summary &&
                (summary.length > 180
                  ? `${summary.slice(0, 180)}...`
                  : summary)}
            </p>
          </div>
        </div>
      </div>
    </div>
  );
};

MovieGroup.propTypes = {
  id: PropTypes.number.isRequired,
  img: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired,
  rating: PropTypes.number,
  runtime: PropTypes.number,
  summary: PropTypes.string,
};

export default MovieGroup;

MovieSearch

import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';

const MovieSearch = ({
  id,
  coverImg,
  title,
  rating,
  runtime,
  year,
  summary,
}) => {
  return (
    <div>
      <div className="show">
        <div className="shortview">
          <div className="shortview-img">
            <img src={coverImg} alt={title} />
          </div>
          <div className="shortview-text">
            <div className="title">
              <h3>
                <Link to={`/movie/${id}`}>
                  {title.length > 35 ? `${title.slice(0, 35)}...` : title}
                </Link>
              </h3>
            </div>
            <div className="info">
              <p>{year && `year: ${year}`}</p>
              <p>{rating && `rating: ${rating} / 10`}</p>
              <p>{runtime && `runtime: ${runtime} min`}</p>
              <p>
                {summary &&
                  (summary.length > 180
                    ? `${summary.slice(0, 180)}...`
                    : summary)}
              </p>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

MovieSearch.propTypes = {
  id: PropTypes.number.isRequired,
  coverImg: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired,
  rating: PropTypes.number,
  runtime: PropTypes.number,
  summary: PropTypes.string,
};

export default MovieSearch;

MovieSlide

import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';

const MovieSlide = ({ id, coverImg, rating, runtime, title }) => {
  return (
    <div>
      <Link to={`/movie/${id}`}>
        <img src={coverImg} alt={title} />
      </Link>
      <div className="text">
        <div className="title">
          <h3>
            <Link to={`/movie/${id}`}>
              {title.length > 35 ? `${title.slice(0, 35)}...` : title}
            </Link>
          </h3>
        </div>
      </div>
      <span>{rating && `rating: ${rating} / 10`}</span>
      <span>{runtime && `runtime: ${runtime} min`}</span>
    </div>
  );
};

MovieSlide.propTypes = {
  id: PropTypes.number.isRequired,
  coverImg: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired,
};

export default MovieSlide;



Routes 작성

Home

import { Link } from 'react-router-dom';
import Slide from '../components/Slide';
import { ImPlay } from 'react-icons/im';
import { Group_obj, Group_key_arr } from '../atom/NavList';

const Home = () => {
  return (
    <div>
      {Group_key_arr.map((group) => (
        <div key={group}>
          <div className="title">
            <Link
              to={`/page/${Group_obj[group]}/1`}
              style={{
                display: 'flex',
                flexDirection: 'row',
                alignContent: 'center',
              }}
            >
              <ImPlay />
              <h3>{group}</h3>
            </Link>
          </div>
          <Slide
            movieApi={`https://yts.mx/api/v2/list_movies.json?limit=10&${Group_obj[group]}&sort_by=rating`}
          />
        </div>
      ))}
      <div className="footer">
        <div className="author">
          <h4>thisisyjin</h4>
        </div>
        <ul>
          <li>githubs</li>
          <li>dev log</li>
          <li>contact</li>
        </ul>
      </div>
    </div>
  );
};

export default Home;

-> 여기에서 Slide 컴포넌트에 movieApi라는 props를 전달해준다.


Detail

import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import MovieDetail from '../components/MovieDetail';
import Load from '../components/Load';

const Detail = () => {
  const { id } = useParams();
  const [loading, setLoading] = useState(true);
  const [movie, setMovie] = useState([]);

  const getMovie = async () => {
    const json = await (
      await fetch(`https://yts.mx/api/v2/movie_details.json?movie_id=${id}`)
    ).json();

    setMovie(json.data.movie);
  };

  useEffect(() => {
    getMovie();
    setLoading(false);
  }, []);

  return (
    <div>
      {loading ? (
        <Load />
      ) : (
        <MovieDetail
          key={movie.id}
          id={movie.id}
          coverImg={movie.medium_cover_image}
          rating={movie.rating}
          runtime={movie.runtime}
          description_full={movie.description_full}
          background_image_original={movie.background_image_original}
          title={movie.title}
          genres={movie.genres}
        />
      )}
    </div>
  );
};

export default Detail;
  • 여기서는 loading이 false면 MovieDetail 컴포넌트를 보여준다.
    -> props를 전부 전달해준다.
    -> useParams로 url 끝에 영화의 id를 기준으로 api를 fetch한다.
    (영화 상세 정보)

    참고
    Home에서 불러온 API는 영화 리스트들이고,
    Detail에서 불러온 API는 영화 각각의 세부 데이터를 불러온 것이다.


Group

import { useEffect, useState } from 'react';
import { useParams, Link } from 'react-router-dom';
import MovieGroup from '../components/MovieGroup';
import Load from '../components/Load';

const List_arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const Group = () => {
  const { group, page } = useParams();
  const [loading, setLoading] = useState(true);
  const [movies, setMovies] = useState([]);

  const getMovies = async () => {
    const json = await (
      await fetch(
        `https://yts.mx/api/v2/list_movies.json?page=${page}&${group}&sort_by=rating`
      )
    ).json();
    setMovies(json.data.movies);
  };

  useEffect(() => {
    getMovies();
    setLoading(false);
    return;
  }, [group, page]); // group이나 page 바뀔때마다

  return (
    <div>
      {loading ? (
        <Load />
      ) : (
        <div>
          {movies.map((movie) => (
            <MovieGroup
              key={movie.id}
              id={movie.id}
              title={movie.title}
              coverImg={movie.medium_cover_image}
              rating={movie.rating}
              runtime={movie.runtime}
              summary={movie.summary}
              year={movie.year}
            />
          ))}
        </div>
      )}
      {loading ? null : (
        <div>
          <div>
            {List_arr.map((lst) => (
              <Link key={lst} to={`/page/${group}/${lst}`}>
                {lst}
              </Link>
            ))}
          </div>
        </div>
      )}
    </div>
  );
};

export default Group;

import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import MovieSearch from '../components/MovieSearch';
import Load from '../components/Load';

const Search = () => {
  const { search } = useParams();
  const [loading, setLoading] = useState(true);
  const [movies, setMovies] = useState([]);
  const [movieArr, setMovieArr] = useState([]);

  const getMovies = () => {
    for (let i = 1; i <= 100; i++) {
      setLoading(true);
      setMovies([]);
      fetch(`https://yts.mx/api/v2/list_movies.json?page=${i}&sort_by=rating`)
        .then((res) => res.json())
        .then((json) => setMovies(json.data.movies));
    }
    setLoading(false);
  };

  useEffect(() => {
    setLoading(true);
    setMovieArr([]);
    getMovies();
    return;
  }, [search]);

  useEffect(() => {
    if (movies.length === 0) {
      return <Load />;
    } else {
      setMovieArr(
        [
          movieArr,
          ...[
            movies.filter(
              (movie) =>
                movie.summary.toLowerCase().indexOf(search.toLowerCase()) !==
                  -1 ||
                movie.description_full
                  .toLowerCase()
                  .indexOf(search.toLowerCase()) !== -1 ||
                movie.title.toLowerCase().indexOf(search.toLowerCase()) !== -1
            ),
          ],
        ]
          .flat()
          .map((movie, i, arr) => {
            for (let j = i + 1; j < arr.length; j++) {
              if (
                movie.id === arr[j].id &&
                arr[j] !== undefined &&
                movie !== undefined
              ) {
                console.log(i, j);
                console.log(movie.id, arr[j].id);
                arr.splice(j, 1);
                j -= 1;
              }
            }
            return movie;
          })
          .sort((a, b) => b['rating'] - a['rating'])
      );
    }
  }, [movies]);

  return (
    <div>
      {loading ? (
        <Load />
      ) : (
        <div>
          {movieArr.map((movie) => (
            <MovieSearch
              key={movie.id}
              id={movie.id}
              title={movie.title}
              coverImg={movie.medium_cover_image}
              rating={movie.rating}
              runtime={movie.runtime}
              summary={movie.summary}
              year={movie.year}
            />
          ))}
        </div>
      )}
    </div>
  );
};

export default Search;

Result 확인


이제 스타일링만 해주면 끝!

profile
기억은 한계가 있지만, 기록은 한계가 없다.

0개의 댓글