-
React - Create MoviePresenterProject using React/Cloning Netflix 2021. 5. 9. 10:47
Movie/MoviePresenter.js
import Helmet from "react-helmet"; import styled from "styled-components"; import PropTypes from "prop-types"; import Loader from "Components/Loader"; import Poster from "Components/Poster"; import Section from "Components/Section"; const Container = styled.div` padding: 10px; `; const MoviePresenter = ({ nowPlaying, upcoming, popular, error, loading }) => ( <> <Helmet> <title>Movies | Nomflix</title> </Helmet> {loading ? ( <Loader /> ) : ( <Container> <Section title="Now Playing"> {nowPlaying.map((movie) => ( <Poster key={movie.id} id={movie.id} imageUrl={movie.poster_path} title={movie.original_title} rating={movie.vote_average} year={movie.release_date.substring(0, 4)} isMovie={true} /> ))} </Section> <Section title="Upcoming"> {upcoming.map((movie) => ( <Poster key={movie.id} id={movie.id} imageUrl={movie.poster_path} title={movie.original_title} rating={movie.vote_average} year={movie.release_date.substring(0, 4)} isMovie={true} /> ))} </Section> <Section title="Popular"> {popular.map((movie) => ( <Poster key={movie.id} id={movie.id} imageUrl={movie.poster_path} title={movie.original_title} rating={movie.vote_average} year={movie.release_date.substring(0, 4)} isMovie={true} /> ))} </Section> </Container> )} </> ); MoviePresenter.propTypes = { nowPlaying: PropTypes.array, popular: PropTypes.array, upcoming: PropTypes.array, loading: PropTypes.bool.isRequired, error: PropTypes.string, }; export default MoviePresenter;
key
nowPlaying, upcoming, popular는 모두 배열이다. 이들을 map을 이용해서 <Poster>를 부르고 있다. 이 때 리스트 안에 있는 <Poster>에 key 값을 추가해야만 한다.
prop-types
설치
$ npm i prop-types
개요
prop-types는 prop의 type을 정의한다. prop을 받을 component 뒤에 method 형식으로 propTypes를 하고 객체를 정의한다.
위 코드와 같이 type을 정의하면 MoviePresenter가 받아들이는 prop이 옳지 않은 type일 때 error를 일으켜 개발자가 실수 하는 것을 막아준다. 만약 React를 typescript로 짰다면 prop-types는 필요가 없다.
Components/Section.js
import PropTypes from "prop-types"; import styled from "styled-components"; const Container = styled.section` :not(:last-child) { margin-bottom: 50px; } `; const Title = styled.h2` font-size: 14px; font-weight: 600; `; const Grid = styled.div` margin-top: 25px; display: grid; grid-template-columns: repeat(auto-fill, 125px); grid-gap: 25px; `; const Section = ({ title, children }) => ( <Container> <Title>{title}</Title> <Grid>{children}</Grid> </Container> ); Section.propTypes = { title: PropTypes.string.isRequired, children: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.node), PropTypes.node, ]), }; export default Section;
children
MoviePresenter가 <Section>에게 준 prop은 title 뿐이다. 하지만 Section에는 children이 정의되어 있다. children은 open component tag와 close component tag 사이에 있는 아이다. MoviePresenter를 보면 <Section></Section> 사이에 <Poster> list가 있다. 이것이 Section에게 children이라는 특별한 prop으로 전달된다.
Components/Poster.js
import styled from "styled-components"; import PropTypes from "prop-types"; const Container = styled.div``; const Image = styled.div` background-image: url(${(props) => props.bgUrl}); width: 100%; height: 180px; background-repeat: no-repeat; background-size: cover; background-position: center; transition: opacity 0.1s linear; `; const Rating = styled.span` bottom: 5px; right: 5px; position: absolute; opacity: 0; transition: opacity 0.1s linear; `; const ImageContainer = styled.div` position: relative; &:hover { ${Image} { opacity: 0.3; } ${Rating} { opacity: 1; } } `; const Title = styled.span` display: block; margin-bottom: 3px; `; const Year = styled.span` font-size: 10px; color: rgba(255, 255, 255, 0.5); `; const Poster = ({ id, imageUrl, title, rating, year }) => ( <Container> <ImageContainer> <Image bgUrl={ imageUrl ? `https://image.tmdb.org/t/p/w300${imageUrl}` : require("../assets/noPosterSmall.png") } /> <Rating> <span role="img" aria-label="rating"> ⭐️ </span>{" "} {rating}/10 </Rating> </ImageContainer> <Title>{title.length > 18 ? `${title.substring(0, 18)}...` : title}</Title> <Year>{year}</Year> </Container> ); Poster.propTypes = { id: PropTypes.number.isRequired, imageUrl: PropTypes.string, title: PropTypes.string.isRequired, rating: PropTypes.number, year: PropTypes.string, isMovie: PropTypes.bool, }; export default Poster;
참고 자료
- 노마드 코더의 React 멤버쉽 강의
- prop-types
- React propTypes 설명
- Children prop
소스 코드
github.com/zpskek/Nomflix-v2/commit/dc55c54f3061bfabf05b2bf2949db4064551612b
'Project using React > Cloning Netflix' 카테고리의 다른 글
React - Search Container and Presenter (0) 2021.05.09 React - Create TV Container and Presenter (0) 2021.05.09 React - Create Loader.js (0) 2021.05.09 React - Create MovieContainer (0) 2021.05.09 React Life cycle Methods (0) 2021.05.09