멋쟁이사자처럼 5주차 과제
7. Practice Movie App
To Do List
import { useState, useEffect } from "react";
function App() {
const [toDo, setToDo] = useState("");
const [toDos, setToDos] = useState([]);
const onChange = (event) => {
setToDo(event.target.value);
};
const onSubmit = (event) => {
event.preventDefault();
if (toDo === "") {
return;
}
setToDos((prev) => [toDo, ...prev]);
setToDo("");
};
return (
<div>
<h1>My To Dos ({toDos.length})</h1>
<form onSubmit={onSubmit}>
<input
onChange={onChange}
value={toDo}
type="text"
placeholder="Write your to do..."
/>
<button>Add To Do</button>
</form>
<hr />
<ul>
{toDos.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
export default App;
- 전부 앞에서 배운 개념들을 활용하였다.
- 추가적으로 배운 개념만 적자면
- state로 배열을 지정할 수 있다는 점과 배열을 변경할 땐 … 표시법 잘 활용하기
- map함수를 이용하여 배열 값들을 활용하기
Coin Tracker
import { useState, useEffect } from "react";
function App() {
const [loading, setLoading] = useState(true);
const [coins, setCoins] = useState([]);
const [selected, setSelected] = useState(false);
const [money, setMoney] = useState(0);
useEffect(() => {
fetch("https://api.coinpaprika.com/v1/tickers")
.then((response) => response.json())
.then((json) => {
setCoins(json);
setLoading(false);
});
}, []);
function onChange(event){
setMoney(event.target.value);
}
function coinChange(event){
setSelected(coins[event.target.selectedIndex])
}
return (
<div>
<h1>The Coins ({coins.length})</h1>
{loading ? <strong>Loading...</strong> : null}
<select onChange={coinChange}>
{coins.map((coin) => (
<option key={coin.id}>
{coin.name} ({coin.symbol}) : ${coin.quotes.USD.price} USD
</option>
))}
</select>
<br></br>
<span>if you have </span>
<input type="number" onChange={onChange} value={money}/><span> $</span>
<br></br>
{selected ? <div><span>{selected.name} : </span><span>{money / selected.quotes.USD.price}</span></div>:null}
</div>
);
}
export default App;
- coin 정보를 불러오는 api를 활용한 실습
- JSON 형태의 데이터를 받아와서 활용해보았다.
- 마지막으로 니꼬쌤이 해보라고 한 도전과제까지 완료
- 내가 입력한 달러에 따라 얼마만큼의 코인을 구매할 수 있는지 구현 완료
Movie App
import PropTypes from "prop-types";
function Movie({coverImg, title, summary, genres}) {
return (
<div>
<img src={coverImg} alt={title} />
<h2>{title}</h2>
<p>{summary}</p>
<ul>
{genres.map((g) => (
<li key={g}>{g}</li>
))}
</ul>
<hr />
</div>
);
Movie.propTypes= {
coverImg:PropTypes.string.isRequired,
title:PropTypes.string.isRequired,
summary:PropTypes.string.isRequired,
genres:PropTypes.arrayOf(PropTypes.string).isRequired,
}
}
export default Movie;
- 하나의 영화 설명 div를 정의한 js파일
- 이 컴포넌트를 호출하는 쪽에서 같이 보낸 props를 활용하여 컴포넌트를 완성한다.
import { useState, useEffect } from "react";
import Movie from "./components/Movie.js";
function Home() {
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?minimum_rating=8.5&sort_by=year"
)
).json();
setMovies(json.data.movies);
setLoading(false);
};
useEffect(() => {
getMovies();
}, []);
return (
<div>
{loading ? (
<h1>Loading...</h1>
) : (
<div>
{movies.map((movie) => (
<Movie
key={movie.id}
coverImg={movie.medium_cover_image}
title={movie.title}
summary={movie.summary}
genres={movie.genres}
/>
))}
</div>
)}
</div>
);
}
export default Home;
- 메인 화면을 나타내는 page
- 영화 api를 통해 데이터를 불러오고 Movie 컴포넌트를 활용해 전체 정보를 출력한다.
- Route 기능은 다음 챕터에!
React Router
import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "./routes/Home.js";
import Detail from "./routes/Detail";
function App() {
return (
<Router>
<Routes>
<Route path="/hello" element=<h1>Hello</h1>/>
<Route path="/movie" element={<Detail />}/>
<Route path="/" element={<Home />}/>
</Routes>
</Router>
);
}
export default App;
- switch는 route를 찾는 역할, route는 url을 의미함
- 강의에서는 Switch를 사용했지만 업데이트 돼서 Route에 element 이렇게 써야 함
- Link를 만들어서 이동하게 하고 싶다 -> 일반적으로 <a href~~>를 쓴다고 생각할 수 있지만 이 경우 페이지 전체가 리로드됨
- a태그 대신에 react-router-dom의 Link를 Movie.js에서 import해서 대신 사용
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
function 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);
setLoading(false);
console.log(movie);
};
useEffect(() => {
getMovie();
}, []);
return (
<div>
{loading ? (
<h1>Loading...</h1>
) : (
<div>
<img src={movie.large_cover_image} alt={movie.title} />
<h2>Title : {movie.title}</h2>
<p>Rating : {movie.rating}</p>
<p>Running Time : {movie.runtime}</p>
<p>Description : {movie.description_full}</p>
</div>
)}
</div>
);
}
export default Detail;
function App() {
return (
<Router>
<Routes>
<Route path="/hello" element=<h1>Hello</h1>/>
<Route path="/movie/:id" element={<Detail />}/>
<Route path="/" element={<Home />}/>
</Routes>
</Router>
);
}
- Detail 페이지로 이어지도록 라우팅 한 뒤, Detail 페이지도 Home 페이지와 비슷하게 구성해두었다.
- path에 :id 같이 작성하여 변수를 받을 수 있다.
- useParams를 사용하여 url parameter를 받을 수 있다.
Publishing
- github pages를 이용해 배포하기
npm i gh-pages npm run build
- pakage.json 파일에 가서 맨 마지막에 콤마 찍고
"homepage":"https://username.github.io/Repositoryname" 추가
- scripts에 추가 (predeploy를 작성하면 위의 build 생략해도 됨
"deploy":"gh-pages -d build",
"predeploy":"npm run build"
npm run deploy 를 실행하자
- 자동으로 우리가 만든 코드를 이용해 배포해준다.
- 배포 완료 github page 최고 React App
- css로 꾸미는건 차근차근해보자
13. Conclusion
- 앞서 했던 deploy와 별 다른 내용이 없어서 따로 정리는 하지 않았다.