<!DOCTYPE html>
<html>
<body>
<span>CLICK COUNTS: 0</span>
<button id="btn">CLICK THIS BUTTON</button>
</body>
<script>
let counter = 0;
const button = document.getElementById("btn");
const span = document.querySelector("span");
function handleClick() {
counter = counter + 1;
span.innerText = `CLICK COUNTS: ${counter}`;
}
button.addEventListener("click", handleClick);
</script>
</html>
HTML을 만든다.
JavaScript를 작성한다. (getElementById, querySelector)
이벤트를 감지한다. (addEventListener)
데이터를 업데이트한다. (counter)
HTML을 업데이트한다. (span.innerText)
https://unpkg.com/react@17.0.2/umd/react.production.min.js
https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js
ReactDOM.render()
render()은 리액트 엘리먼트를 HTML로 만들어 배치한다는 것을 의미한다.
<!DOCTYPE html>
<html>
<head></head>
<body style>
<div id="root">
<span></span>
</div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script>
const root = document.getElementById("root");
const span = React.createElement(
"span",
{ id: "sexy-span", style: { color: "red" } },
"Hello World"
);
ReactDOM.render(span, root);
</script>
</html>
<!DOCTYPE html>
<html>
<head></head>
<body style>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script>
const root = document.getElementById("root");
const h3 = React.createElement(
"h3",
{
id: "title",
onMouseEnter: () => console.log("MOUSE ENTER")
},
"HELLO WORLD"
);
const btn = React.createElement(
"button",
{
onClick: () => console.log("CLICKED"),
style: {
backgroundColor: "grey"
}
},
"CLICK THIS BUTTON"
);
const container = React.createElement("div", null, [h3, btn]);
ReactDOM.render(container, root);
</script>
</html>
React JS와 ReactDOM을 import한다.
React JS는 엘리먼트를 생성하고 이벤트 리스너를 더할 수 있도록 한다.
ReactDOM은 엘리먼트들을 HTML에 렌더링할 수 있도록 한다.
ReactDOM은 container 엘리먼트를 body의 div id="root"는 렌더링하도록 한다.
JSX는 JavaScript를 확장한 문법이다.
<!DOCTYPE html>
<html>
<head></head>
<body style>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
const Title = (
<h3 id="title" onMouseEnter={ () => console.log("MOUSE ENTER") }>
HELLO WORLD
</h3>
);
const Button = <button style={{
backgroundColor: "green",
}} onClick={ () => console.log("CLICKED") }>
CLICK THIS BUTTON
</button>;
const container = React.createElement("div", null, [Title, Button]);
ReactDOM.render(container, root);
</script>
</html>
<!DOCTYPE html>
<html>
<head></head>
<body style>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
function Title() {
return <h3 id="title" onMouseEnter={ () => console.log("MOUSE ENTER") }>
HELLO WORLD
</h3>
};
function Button() {
return <button style={{
backgroundColor: "green",
}}
onClick={ () => console.log("CLICKED")
}>
CLICK THIS BUTTON
</button>
};
const Container = (
<div>
<Title />
<Button />
</div>
);
ReactDOM.render(Container, root);
</script>
</html>
<!DOCTYPE html>
<html>
<head></head>
<body style>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
let counter = 0;
function render() {
ReactDOM.render(<Container />, root);
}
function countUp() {
counter = counter + 1;
// ReactDOM.render(<Container />, root);
render();
}
function Button() {
return <button
style={{
backgroundColor: "green",
}}
onClick={ () => console.log("CLICKED")
}
>
CLICK THIS BUTTON
</button>
}
function Container() {
return (
<div>
<h3>CLICK COUNT: { counter }</h3>
<button onClick={ countUp }>CLICK THIS BUTTON</button>
</div>
);
}
render();
</script>
</html>
<!DOCTYPE html>
<html>
<head></head>
<body style>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
function render() {
ReactDOM.render(<Container />, root);
}
let counter = 0;
function App() {
const [counter, modifier] = React.useState(0);
counter = 0;
return (
<div>
<h3>CLICK COUNT: { counter }</h3>
<button>CLICK THIS BUTTON</button>
</div>
);
}
render();
</script>
</html>
<!DOCTYPE html>
<html>
<head></head>
<body style>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
function render() {
ReactDOM.render(<App />, root);
}
function App() {
const [counter, setCounter] = React.useState(0);
const onClick = () => {
setCounter(counter + 1);
};
return (
<div>
<h3>CLICK COUNT: { counter }</h3>
<button onClick={ onClick }>CLICK THIS BUTTON</button>
</div>
);
}
render();
</script>
</html>
<!DOCTYPE html>
<html>
<head></head>
<body style>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
function render() {
ReactDOM.render(<App />, root);
}
function App() {
const [counter, setCounter] = React.useState(0);
const onClick = () => {
setCounter(counter + 1);
};
console.log("Rendered");
console.log(counter);
return (
<div>
<h3>CLICK COUNT: { counter }</h3>
<button onClick={ onClick }>CLICK THIS BUTTON</button>
</div>
);
}
render();
</script>
</html>
<!DOCTYPE html>
<html>
<head></head>
<body style>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
function render() {
ReactDOM.render(<App />, root);
}
function App() {
const [counter, setCounter] = React.useState(0);
const onClick = () => {
setCounter((current) => current + 1);
};
console.log("Rendered");
console.log(counter);
return (
<div>
<h3>CLICK COUNT: { counter }</h3>
<button onClick={ onClick }>CLICK THIS BUTTON</button>
</div>
);
}
render();
</script>
</html>
<!DOCTYPE html>
<html>
<head></head>
<body style>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const root = document.getElementById("root");
function render() {
ReactDOM.render(<App />, root);
}
function App() {
const [minutes, setMinutes] = React.useState();
const onChange= () => {
console.log("UPDATED");
setMinutes(event.target.value);
};
return (
<div>
<h1 className="title">TIME CONVERTER</h1>
<label htmlFor="minutes"> MINUTES </label>
<input
id="minutes"
value={ minutes }
placeholder="MINUTES"
type="number"
onChange={ onChange }
/>
<h4>MINUTES TO CONVERT: { minutes }</h4>
<label htmlFor="hours"> HOURS </label>
<input id="hours" placeholder="hours" type="number" />
</div>
);
}
render();
</script>
</html>
<!DOCTYPE html>
<html>
<head></head>
<body style>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
function App() {
const [time, setTime] = React.useState();
const [inverted, setInverted] = React.useState(false);
const onChange= (event) => {
console.log("UPDATED");
setTime(event.target.value);
};
const reset = () => setTime(0);
const onInvert = () => {
reset();
setInverted((current) => !current);
}
return (
<div>
<div>
<h1 className="title">TIME CONVERTER</h1>
<label htmlFor="minutes"> MINUTES </label>
<input
id="minutes"
type="number"
value={ inverted ? (time * 60) : time }
placeholder="MINUTES"
onChange={ onChange }
disabled={ inverted }
/>
</div>
<div>
<label htmlFor="hours"> HOURS </label>
<input
id="hours"
type="number"
value={ inverted ? time : Math.round(time / 60) }
placeholder="HOURS"
onChange = { onChange }
disabled={ !inverted }
/>
</div>
<p>
<button onClick={ reset }>RESET</button>
<button onClick={ onInvert }>INVERT</button>
</p>
</div>
);
}
ReactDOM.render(<App />, root);
</script>
</html>
<!DOCTYPE html>
<html>
<head></head>
<body style>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
function TimeConverter() {
const [time, setTime] = React.useState();
const [inverted, setInverted] = React.useState(false);
const onChange = (event) => {
console.log("UPDATED");
setTime(event.target.value);
};
const reset = () => setTime(0);
const onInvert = () => {
reset();
setInverted((current) => !current);
};
return (
<div>
<div>
<h1 className="title">TIME CONVERTER</h1>
<label htmlFor="minutes"> MINUTES </label>
<input
id="minutes"
type="number"
value={ inverted ? (time * 60) : time }
placeholder="MINUTES"
onChange={ onChange }
disabled={ inverted }
/>
</div>
<div>
<label htmlFor="hours"> HOURS </label>
<input
id="hours"
type="number"
value={ inverted ? time : Math.round(time / 60) }
placeholder="HOURS"
onChange = { onChange }
disabled={ !inverted }
/>
</div>
<p>
<button onClick={ reset }> RESET </button>
<button onClick={ onInvert }> INVERT </button>
</p>
</div>
);
}
function DistanceConverter() {
const [distance, setDistance] = React.useState();
const [inverted, setInverted] = React.useState(false);
const onChange = (event) => {
console.log("UPDATED");
setDistance(event.target.value);
};
const reset = () => setDistance(0);
const onInvert = () => {
reset();
setInverted((current) => !current);
};
return (
<div>
<div>
<h1 className="title">DISTANCE CONVERTER</h1>
<label htmlFor="kilometers"> KILOMETERS </label>
<input
id="kilometers"
type="number"
value={ inverted ? (distance * 1.6) : distance }
placeholder="KILOMETERS"
onChange={ onChange }
disabled={ inverted }
/>
</div>
<div>
<label htmlFor="miles"> MILES </label>
<input
id="miles"
types="number"
value={ inverted ? distance : Math.round(distance / 1.6) }
placeholder="MILES"
onChange={ onChange }
disabled={ !inverted }
/>
</div>
<p>
<button onClick={ reset }> RESET </button>
<button onClick={ onInvert }> INVERT </button>
</p>
</div>
);
}
function App() {
const [index, setIndex] = React.useState(0);
const onSelect = (event) => {
console.log(event.target.value);
setIndex(event.target.value);
}
return (
<div>
<h1 className="title">VERSATILE CONVERTER</h1>
<select value={ index } onChange={ onSelect }>
<option value="0">DEFAULT</option>
<option value="1">TIME CONVERTER</option>
<option value="2">DISTANCE CONVERTER</option>
</select>
<hr />
{ index === "1" ? <TimeConverter /> : null }
{ index === "2" ? <DistanceConverter /> : null }
</div>
);
}
const root = document.getElementById("root");
ReactDOM.render(<App />, root);
</script>
</html>
<!DOCTYPE html>
<html>
<head></head>
<body style>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
function Button(props) {
console.log(props.text, props.isLarge);
return (
<button
style={{
backgroundColor: "grey",
color: "white",
padding: "10px 20px",
border: 0,
borderRadius: 10,
fontSize: props.isLarge ? 20 : 10
}}
>
{ props.text }
</button>
);
}
function App() {
return (
<div>
<Button text="SAVE" isLarge={ false } />
<Button text="CONFIRM" isLarge={ true } />
</div>
);
}
const root = document.getElementById("root");
ReactDOM.render(<App />, root);
</script>
</html>
<!DOCTYPE html>
<html>
<head></head>
<body style>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
function Button({ text, changeValue, fontSize }) {
console.log(text, "RENDERED")
return (
<button
style={{
backgroundColor: "grey",
color: "white",
padding: "10px 20px",
border: 0,
borderRadius: 10,
fontSize: fontSize
}}
onClick={ changeValue }
>
{ text }
</button>
);
}
function App() {
const [value, setValue] = React.useState("SAVE");
const changeValue = () => setValue("REVERT");
return (
<div>
<MemorizedButton text={ value } changeValue={ changeValue } fontSize={ 20 } />
<MemorizedButton text="CONFIRM" fontSize={ 20 } />
</div>
);
}
const MemorizedButton = React.memo(Button);
const root = document.getElementById("root");
ReactDOM.render(<App />, root);
</script>
</html>
<!DOCTYPE html>
<html>
<head></head>
<body style>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/prop-types@15.7.2/prop-types.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
function Button({ text, changeValue, fontSize }) {
console.log(text, "RENDERED")
return (
<button
style={{
backgroundColor: "grey",
color: "white",
padding: "10px 20px",
border: 0,
borderRadius: 10,
fontSize: fontSize
}}
onClick={ changeValue }
>
{ text }
</button>
);
}
Button.propTypes = {
text: PropTypes.string,
fontSize: PropTypes.number,
};
function App() {
const [value, setValue] = React.useState("SAVE");
const changeValue = () => setValue("REVERT");
return (
<div>
<MemorizedButton text={ value } changeValue={ changeValue } fontSize={ 20 } />
<MemorizedButton text="CONFIRM" fontSize={ 20 } />
</div>
);
}
const MemorizedButton = React.memo(Button);
const root = document.getElementById("root");
ReactDOM.render(<App />, root);
</script>
</html>
import Button from "./Button.js";
import styles from "./App.module.css";
function App() {
return (
<div>
<h1 className={styles.title}>CREATE-REACT-APP</h1>
<Button text={"CONTINUE"} />
</div>
);
}
export default App;
import PropTypes from "prop-types";
import styles from "./Button.module.css";
function Button({ text }) {
return <button className={styles.button}>{text}</button>
}
Button.propTypes = {
text: PropTypes.string.isRequired,
}
export default Button;
.button {
color: white;
background-color: lightslategrey;
border: 1;
border-radius: 10px;
font-size: large;
}
.title {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
font-size: 20px;
}
import React, { useState } from "react";
function App() {
const [counter, setValue] = React.useState(0);
const onClick = () => setValue((prev) => prev + 1);
console.log("RENDERED");
return (
<div>
<h1>{counter}</h1>
<button onClick={onClick}>COUNTER</button>
</div>
);
}
export default App;
import React, { useState, useEffect } from "react";
function App() {
const [counter, setValue] = React.useState(0);
const onClick = () => setValue((prev) => prev + 1);
console.log("RENDERED CONSTANTLY");
const renderTemporary = () => {
console.log("RENDERED TEMPORARILY");
}
useEffect(renderTemporary, []);
return (
<div>
<h1>{counter}</h1>
<button onClick={onClick}>COUNTER</button>
</div>
);
}
export default App;
import React, { useState, useEffect } from "react";
function App() {
const [counter, setValue] = React.useState(0);
const [keyword, setKeyword] = React.useState("");
const onClick = () => setValue((prev) => prev + 1);
const onChange = (event) => setKeyword(event.target.value);
// console.log("RENDERED CONSTANTLY");
useEffect(() => {
console.log("RENDERED TEMPORARILY");
}, []);
useEffect(() => {
console.log("COUNTER UPDATED");
}, [counter]);
useEffect(() => {
console.log("SEARCHED", keyword);
}, [keyword]);
// useEffect(() => {
// console.log("COUNTER UPDATED AND SEARCHED");
// }, [counter, keyword]);
return (
<div>
<input
type="text"
value={keyword}
placeholder="SEARCH"
onChange={onChange}
/>
<h1>{counter}</h1>
<button onClick={onClick}>COUNTER</button>
</div>
);
}
export default App;
import React, { useState, useEffect } from "react";
function EffectFunc() {
useEffect(function () {
console.log("CREATED");
return function () {
console.log("ELIMINATED");
};
}, []);
return <h1>HELLO</h1>;
}
function App() {
const [showing, setShowing] = useState(false);
const onClick = () => setShowing((prev) => !prev);
return (
<div>
{showing ? <EffectFunc /> : null}
<button onClick={onClick}>{showing ? "HIDE" : "SHOW"}</button>
</div>
);
}
export default App;
import { useState } from "react";
function App() {
const [todo, setTodo] = useState("");
const [todoList, setTodoList] = useState([]);
const onChange = (event) => setTodo(event.target.value);
const onSubmit = (event) => {
event.preventDefault();
if (todo === "") {
return;
}
setTodoList(currentTodoList => [todo, ...currentTodoList])
setTodo("");
};
console.log(todoList);
return (
<div>
<h1>TO-DO LIST ({todoList.length})</h1>
<form onSubmit={onSubmit}>
<input
type="text"
placeholder="FILL IN YOUR LIST"
value={todo}
onChange={onChange}
/>
<button>ADD TO-DO</button>
</form>
</div>);
}
export default App;
import { useState, useEffect } from "react";
function App() {
const [loading, setLoading] = useState(true);
const [crypto, setCrypto] = useState([])
useEffect(() => {
fetch("https://api.coinpaprika.com/v1/tickers")
.then((response) => response.json())
.then((json) => {
setCrypto(json);
setLoading(false);
});
}, []);
return (
<div>
<h1>CRYPTOCURRENCY {loading ? "" : `(${crypto.length})`}</h1>
{loading ? (
<strong>LOADING...</strong>
) : (
<select>
{crypto.map((crypto) => (
<option>
{crypto.name} ({crypto.symbol}): ${crypto.quotes.USD.price}
</option>
))}
</select>
)}
</div>
);
}
export default App;
import { useState, useEffect } from "react";
function App () {
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=9.0&sort_by=year`
)
).json();
setMovies(json.data.movies);
setLoading(false);
}
useEffect(() => {
getMovies();
}, []);
console.log(movies);
return (
<div>
{loading ? (
<h1>LOADING...</h1>
) : (
<div>
{movies.map((movie) => (
<div key={movie.id}>
<img src={movie.medium_cover_image} />
<h2>{movie.title}</h2>
<p>{movie.summary}</p>
<ul>
{movie.genres.map((genre) => (
<li key={genre}>{genre}</li>
))}
</ul>
</div>
))}
</div>
)}
</div>
);
}
export default App;
function App () {
return null;
}
export default App;
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=9.0&sort_by=year`
)
).json();
setMovies(json.data.movies);
setLoading(false);
}
useEffect(() => {
getMovies();
}, []);
console.log(movies);
return (
<div>
{loading ? (
<h1>LOADING...</h1>
) : (
<div>
{movies.map((movie) => (
<Movie
key={movie.id}
coverImage={movie.medium_cover_image}
title={movie.title}
summary={movie.summary}
genres={movie.genres}
/>
))}
</div>
)}
</div>
);
}
export default Home;
import PropTypes from "prop-types";
function Movie({ coverImage, title, summary, genres }) {
return (
<div>
<img src={coverImage} alt={title} />
<h2>{title}</h2>
<p>{summary}</p>
<ul>
{genres.map((genre) => (
<li key={genre}>{genre}</li>
))}
</ul>
</div>
);
}
Movie.propTypes = {
coverImage: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
summary: PropTypes.string.isRequired,
genres: PropTypes.arrayOf(PropTypes.string).isRequired,
};
export default Movie;
function Detail() {
return <h1>Detail</h1>;
}
export default Detail;
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "./routes/Home";
import Detail from "./routes/Detail";
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/movies" element={<Detail />} />
</Routes>
</Router>
);
}
export default App;
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=9.0&sort_by=year`
)
).json();
setMovies(json.data.movies);
setLoading(false);
}
useEffect(() => {
getMovies();
}, []);
console.log(movies);
return (
<div>
{loading ? (
<h1>LOADING...</h1>
) : (
<div>
{movies.map((movie) => (
<Movie
key={movie.id}
coverImage={movie.medium_cover_image}
title={movie.title}
summary={movie.summary}
genres={movie.genres}
/>
))}
</div>
)}
</div>
);
}
export default Home;
function Detail() {
return <h1>Detail</h1>;
}
export default Detail;
import { Link } from "react-router-dom";
import PropTypes from "prop-types";
function Movie({ coverImage, title, summary, genres }) {
return (
<div>
<img src={coverImage} alt={title} />
<h2>
<Link to="/movies">{title}</Link>
</h2>
<p>{summary}</p>
<ul>
{genres.map((genre) => (
<li key={genre}>{genre}</li>
))}
</ul>
</div>
);
}
Movie.propTypes = {
coverImage: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
summary: PropTypes.string.isRequired,
genres: PropTypes.arrayOf(PropTypes.string).isRequired,
};
export default Movie;
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "./routes/Home";
import Details from "./routes/Details";
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/movies/:id" element={<Details />} />
</Routes>
</Router>
);
}
export default App;
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=9.0&sort_by=year`
)
).json();
setMovies(json.data.movies);
setLoading(false);
}
useEffect(() => {
getMovies();
}, []);
console.log(movies);
return (
<div>
{loading ? (
<h1>LOADING...</h1>
) : (
<div>
{movies.map((movie) => (
<Movie
key={movie.id}
id={movie.id}
title={movie.title}
summary={movie.summary}
genres={movie.genres}
coverImage={movie.medium_cover_image}
/>
))}
</div>
)}
</div>
);
}
export default Home;
import { useEffect } from "react";
import { useParams } from "react-router-dom";
function Details() {
const { id } = useParams();
const getMovie = async () => {
const json = await (
await fetch(`https://yts.mx/api/v2/movie_details.json?movie_id=${id}`)
).json();
console.log(json);
};
useEffect(() => {
getMovie();
}, []);
return <h1>MOVIE DETAILS</h1>;
}
export default Details;
import { Link } from "react-router-dom";
import PropTypes from "prop-types";
function Movie({ id, title, summary, genres, coverImage }) {
return (
<div>
<img src={coverImage} alt={title} />
<h2>
<Link to={`/movies/${id}`}>{title}</Link>
</h2>
<p>{summary}</p>
<ul>
{genres.map((genre) => (
<li key={genre}>{genre}</li>
))}
</ul>
</div>
);
}
Movie.propTypes = {
id: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
summary: PropTypes.string.isRequired,
genres: PropTypes.arrayOf(PropTypes.string).isRequired,
coverImage: PropTypes.string.isRequired,
};
export default Movie;
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import "./styles.css";
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "./routes/Home";
import Details from "./routes/Details";
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/movies/:id" element={<Details />} />
</Routes>
</Router>
);
}
export default App;
import { useState, useEffect } from "react";
import Movie from "../components/Movie.js";
import styles from "./Home.module.css";
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=9.0&sort_by=year`
)
).json();
setMovies(json.data.movies);
setLoading(false);
}
useEffect(() => {
getMovies();
}, []);
console.log(movies);
return (
<div className={styles.container}>
{loading ? (
<div className={styles.loader}>
<span>LOADING...</span>
</div>
) : (
<div className={styles.movies}>
{movies.map((movie) => (
<Movie
key={movie.id}
id={movie.id}
title={movie.title}
year={movie.year}
summary={movie.summary}
genres={movie.genres}
coverImage={movie.medium_cover_image}
/>
))}
</div>
)}
</div>
);
}
export default Home;
import { useEffect } from "react";
import { useParams } from "react-router-dom";
function Details() {
const { id } = useParams();
const getMovie = async () => {
const json = await (
await fetch(`https://yts.mx/api/v2/movie_details.json?movie_id=${id}`)
).json();
console.log(json);
};
useEffect(() => {
getMovie();
}, []);
return <h1>MOVIE DETAILS</h1>;
}
export default Details;
import { Link } from "react-router-dom";
import PropTypes from "prop-types";
import styles from "./Movie.module.css";
function Movie({ id, title, year, summary, genres, coverImage }) {
return (
<div className={styles.movie}>
<img className={styles.movie_image} src={coverImage} alt={title} />
<div>
<h2 className={styles.movie_title}>
<Link to={`/movies/${id}`}>{title}</Link>
</h2>
<h3 className={styles.movie_year}>{year}</h3>
<p className={styles.movie_summary}>{summary.length > 235 ? `${summary.slice(0, 235)}...` : summary}</p>
<ul className={styles.movie_genres}>
{genres.map((genre) => (
<li key={genre}>{genre}</li>
))}
</ul>
</div>
</div>
);
}
Movie.propTypes = {
id: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
summary: PropTypes.string.isRequired,
genres: PropTypes.arrayOf(PropTypes.string).isRequired,
coverImage: PropTypes.string.isRequired,
};
export default Movie;
* {
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%;
}
.container {
height: 100%;
display: flex;
justify-content: center;
}
.loader {
height: 100vh;
width: 100%;
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%;
}
}
.movie {
background-color: white;
margin-bottom: 70px;
font-weight: 300;
padding: 20px;
border-radius: 5px;
color: #adaeb9;
display: grid;
grid-template-columns: minmax(150px, 1fr) 2fr;
grid-gap: 20px;
text-decoration: none;
color: inherit;
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);
}
.movie_image {
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_title a {
margin-bottom: 5px;
font-size: 24px;
color: #2c2c2c;
text-decoration: none;
}
.movie_genres {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-wrap: wrap;
margin: 5px 0px;
font-weight: 500;
}
.movie_summary {
font-weight: 300;
}
.movie_genres li,
.movie_year {
margin-right: 10px;
font-size: 14px;
}