왜 리액트를 쓰는가?
사용자 경험이 좋다.
데이터랑 화면의 일치가 쉽다.
중복을 피할 수 있다.
리액트는 자바스크립트를 사용한다.
html 파일에 작성하지 않는 내용도 react는 자바스크립트로 만든 모든 요소를 생성해 html에 집어넣는다.
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
소스코드에는 빈 index.html파일만 보인다.
react는 소스코드에 처음부터 html를 넣지 않는다. 빈 html을 불러와서 컴포넌트에 작성한 것들을 추가하거나 제거한다.
virtual DOM, 존재하지 않는 소스코드를 react가 만들어 낸다.
//컴포넌트
ReactDOM.render(<App />, document.getElementById('root'));
//potato.js
import React from 'react';
function Potato(){
return <h3>I love poooo</h3>;
}
export default Potato;
//app.js
import React from 'react';
import Potato from './potato';
function App() {
return (
<div className="App">
<Potato />
<h1>aaa</h1>hello!
</div>
);
}
export default App;
웹팩 : 쪼개진 자바스크립트 파일을 html이 사용할 수 있도록 합쳐준다.
jsx에서는 컴포넌트에 정보를 보낼 수 있다.
function Food(prop){
return (
<h1>i like {prop.fav}</h1>
)
}
function App() {
return (
<div>
<h1>Hello!</h1>
<Food fav="kimchi"/>
</div>
);
}
const foodarray = [
{name : "kimchi",
describe : "spicy"},
{name : "ramen",
describe : "yummy"},
{name : "chicken",
describe : "hot"},
{name : "kimbam",
describe : "delicious"}
];
foodarray.map(current =>{
alert(current.name + " " + current.describe);
})
/* foodarray.map(funtion(current)
{
alert(current.name + " " + current.describe);
})*/
import logo from './logo.svg';
import './App.css';
function Food(prop){
return (
<h1>i like {prop.name} {prop.name} is {prop.describe}</h1>
)
}
const foodarray = [
{name : "kimchi",
describe : "spicy"},
{name : "ramen",
describe : "yummy"},
{name : "chicken",
describe : "hot"},
{name : "kimbam",
describe : "delicious"}
];
function App() {
return (
<div>
<h1>Hello!</h1>
{foodarray.map(item => <Food name={item.name} describe={item.describe}/>)}
</div>
);
}
export default App;
function Food(prop){
return (
<h1>i like {prop.name} {prop.name} is {prop.describe}</h1>
)
}
const foodarray = [
{name : "kimchi",
describe : "spicy"},
{name : "ramen",
describe : "yummy"},
{name : "chicken",
describe : "hot"},
{name : "kimbam",
describe : "delicious"}
];
function renderFood(dish){
return <Food name={dish.name} describe={dish.describe}/>
}
function App() {
return (
<div>
<h1>Hello!</h1>
{foodarray.map(renderFood)}
</div>
);
}
Each child in a list should have a unique "key" prop. ⇒ 모든 react의 컴포넌트는 유일해야 하는데, 리스트 안에 집어 놓으면 유일성을 잃어버리게 된다.
npm i prop-types
Food.propTypes = {
name:PropTypes.string.isRequired,
describe:PropTypes.string.isRequired,
rating:PropTypes.number.isRequired
}
컴포넌트의 prop
.PropTypes.기대하는 자료형
.isRequiredclass App extends React.Component{
}
import React from 'react';
class App extends React.Component {
state = {
count : 0
};
render() {
return <div>
<h1>count : {this.state.count}</h1>
</div>
}
}
export default App;
import React from 'react';
class App extends React.Component {
state = {
count : 0
};
add = () =>{
console.log("add");
};
minus = () => {
console.log("minus");
};
render() {
return <div>
<h1>count : {this.state.count}</h1>
<button onClick={this.add}>plus</button>
<button onClick={this.minus}>Minus</button>
</div>
}
}
export default App;
함수 = () ⇒ {}
== 함수(){}
import React from 'react';
class App extends React.Component {
state = {
count : 0
};
add = () =>{
this.setState({count : this.state.count + 1})
};
minus = () => {
this.setState({count : this.state.count - 1})
};
render() {
return <div>
<h1>count : {this.state.count}</h1>
<button onClick={this.add}>plus</button>
<button onClick={this.minus}>Minus</button>
</div>
}
}
export default App;
this.setState(current => ({count : current.count + 1}))
import React from 'react';
class App extends React.Component {
state = {
count : 0,
data : 0
};
add = () =>{
this.setState(current => ({count : current.count + 1}))
};
minus = () => {
this.setState(current => ({count : current.count - 1}))
};
constructor(props) {
super(props);
console.log("constructor");
}
componentDidMount()
{
console.log("first rendering");
}
componentDidUpdate()
{
console.log("just update");
}
componentWillUnmount()
{
console.log("component die");
}
render() {
console.log("render");
return <div>
<h1>count : {this.state.count}</h1>
<button onClick={this.add}>plus</button>
<button onClick={this.minus}>Minus</button>
</div>
}
}
export default App;
import React from 'react';
class App extends React.Component {
state = {
isLoading: true
}
componentDidMount() {
setTimeout(() => {
this.setState({isLoading : false});
}, 3000);
}
render(){
const {isLoading} = this.state;
return <div>
<h1>{isLoading ? "Loading" : "Ready"}</h1>
</div>;
}
}
export default App;
axios와 fetch의 차이점
axios, fetch, ajax
import React from 'react';
import axios from "axios";
class App extends React.Component {
state = {
isLoading: true
}
getMovies = async() => { //async getMovies()
const movies = await axios.get("https://yts-proxy.now.sh/list_movies.json");
}
componentDidMount() {
this.getMovies();
}
render(){
const {isLoading} = this.state;
return <div>
<h1>{isLoading ? "Loading" : "Ready"}</h1>
</div>;
}
}
export default App;
import React from "react"
import PropTypes from "prop-types"
function Movie({ id, year, title, summary, poster }) {
return <h4>{title}</h4>;
}
Movie.propTypes =
{
id: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
summary: PropTypes.string.isRequired,
poster: PropTypes.string.isRequired,
year: PropTypes.number.isRequired
};
export default Movie;
import React from 'react';
import axios from "axios";
import Movie from './movie';
class App extends React.Component {
state = {
isLoading: true,
movies: []
}
getMovies = async() => { //async getMovies()
const {data: {data :{movies}}} = await axios.get("https://yts-proxy.now.sh/list_movies.json?sort_by=rating");
this.setState({movies, isLoading: false});
}
componentDidMount() {
this.getMovies();
}
render(){
const {isLoading, movies} = this.state;
return <div>
<h1>{isLoading ? "Loading" : movies.map(movie => {
console.log(movie);
return <Movie
key={movie.id}
id={movie.id}
title={movie.title}
year={movie.year}
summary={movie.summary}
poster={movie.medium_cover_image}
/>
})}</h1>
</div>;
}
}
export default App;
key
가 있어야 한다?import React from 'react';
import axios from "axios";
import Movie from './movie';
class App extends React.Component {
state = {
isLoading: true,
movies: []
}
getMovies = async() => { //async getMovies()
const {data: {data :{movies}}} = await axios.get("https://yts-proxy.now.sh/list_movies.json?sort_by=rating");
this.setState({movies, isLoading: false});
}
componentDidMount() {
this.getMovies();
}
render(){
const {isLoading, movies} = this.state;
return (
<section class="container">
{isLoading ? (
<div class="loader">
<span class="loader__text">Loading...</span>
</div>
) : (
<div class="movies">
{movies.map(movie => (
<Movie
key={movie.id}
id={movie.id}
title={movie.title}
poster={movie.medium_cover_image}
summary={movie.summary}
year={movie.year}
/>
))}
</div>
)}
</section>
);
}
}
export default App;
import React from "react"
import PropTypes from "prop-types"
function Movie({ id, year, title, summary, poster }) {
return <div class="movies__movie">
<img src={poster} alt={title} title={title}/>
<div class="movie__data">
<h3 class="movie__title">{title}</h3>
<h5 class="movie__year">{year}</h5>
<p class="movie__summary">{summary}</p>
</div>
</div>;
}
Movie.propTypes =
{
id: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
summary: PropTypes.string.isRequired,
poster: PropTypes.string.isRequired,
year: PropTypes.number.isRequired
};
export default Movie;
import React from 'react';
import axios from "axios";
import Movie from './movie';
import "./App.css"
class App extends React.Component {
state = {
isLoading: true,
movies: []
}
getMovies = async() => { //async getMovies()
const {data: {data :{movies}}} = await axios.get("https://yts-proxy.now.sh/list_movies.json?sort_by=rating");
this.setState({movies, isLoading: false});
}
componentDidMount() {
this.getMovies();
}
render(){
const {isLoading, movies} = this.state;
return (
<section className="container">
{isLoading ? (
<div className="loader">
<span className="loader__text">Loading...</span>
</div>
) : (
<div className="movies">
{movies.map(movie => (
<Movie
key={movie.id}
id={movie.id}
title={movie.title}
poster={movie.medium_cover_image}
summary={movie.summary}
year={movie.year}
genres={movie.genres}
/>
))}
</div>
)}
</section>
);
}
}
export default App;
import React from "react"
import PropTypes from "prop-types"
import "./movie.css"
function Movie({ id, year, title, summary, poster, genres }) {
return <div className="movies__movie">
<img src={poster} alt={title} title={title}/>
<div className="movie__data">
<h3 className="movie__title">{title}</h3>
<h5 className="movie__year">{year}</h5>
<ul className="genres">
{genres.map((genre, index) => (
<li key={index}className="movie__genres">{genre}</li>))}
</ul>
<p className="movie__summary">{summary}</p>
</div>
</div>;
}
Movie.propTypes =
{
id: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
summary: PropTypes.string.isRequired,
poster: PropTypes.string.isRequired,
year: PropTypes.number.isRequired,
genres: PropTypes.arrayOf(PropTypes.string).isRequired
};
export default Movie;
문자열
.slice(시작위치
, 종료위치
)gh-pages
npm i gh-pages
깃허브에 업로드하는 것을 허가해주는 모듈
웹사이트를 깃허브의 깃허브 페이지 도메인에 나타나게 해준다.
{
"name": "react_movie",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3",
"axios": "^0.21.1",
"gh-pages": "^3.2.3",
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3",
"web-vitals": "^1.1.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"deploy": "gh-pages -d build",
"predeploy": "npm run build"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"homepage": "https://gulsam00.github.io/react_movie/"
}
package.json
업로드를 위해 package.json에서 homepage를 설정해준다.
"homepage": "git remote add origin [https://github.io/GulSam00/react_movie.git](https://github.io/GulSam00/react_movie.git)"
script에 "deploy": "gh-pages -d build"
를 추가한다.
script에 "predeploy" : "npm run build"
를 추가한다.
npm build 시 build 폴더가 추가된다.
npm run deploy로 실행한다.
주소 : https://`깃허브id`.github.io/`디렉토리 이름`
'react-scripts'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다.
시 npm update
import React from 'react';
import {HashRouter, Route} from "react-router-dom";
import Home from "./routes/Home";
import About from "./routes/About"
function App(){
return <HashRouter>
<Route path="/" exact={true} component={Home}/>
<Route path="/about" component={About}/>
</HashRouter>
}
export default App;
Route 컴포넌트 안에는 렌더링할 스크린, url로 뭘 할건지가 prop으로 들어간다.
? 왜 path에서도 소문자냐? 기준이 뭐지?
url의 뒤에 About을 붙이면 해당 페이지로 이동
라우터를 여러개 만들면 모두 렌더링하게 된다.
url이 일치하는 부분이 있으면 전부 렌더링하게 된다.
/home, /home/intro 두 개의 path가 있다면 url이 home/intro 일 시 두 개의 컴포넌트 전부 렌더링하게 된다.
exact={true} 시 해당 path만 존재할 시 렌더링하도록 제어할 수 있다.
import React from 'react';
function Navigation() {
return <div>
<a href="/">Home</a>
<a href="/about">About</a>
</div>
}
export default Navigation;
html인 href를 사용하면 react임에도 불구하고 페이지를 이동할 때마다 새로고침을 해버린다.
import React from 'react';
import {Link} from "react-router-dom"
function Navigation() {
return <div>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</div>
}
export default Navigation;
import React from "react"
import PropTypes from "prop-types"
import "./movie.css"
import {Link} from "react-router-dom"
function Movie({ id, year, title, summary, poster, genres }) {
return (
<Link to={{
pathname: "/movie-detail",
state: {
year,
title,
summary,
poster,
genres
}
}}
>
<div className="movies__movie">
<img src={poster} alt={title} title={title}/>
<div className="movie__data">
<h3 className="movie__title">{title}</h3>
<h5 className="movie__year">{year}</h5>
<ul className="genres">
{genres.map((genre, index) => (
<li key={index}className="movie__genres">{genre}</li>))}
</ul>
<p className="movie__summary">{summary.slice(0, 140)}... <button>see more</button></p>
</div>
</div>
</Link>
);
}
Movie.propTypes =
{
id: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
summary: PropTypes.string.isRequired,
poster: PropTypes.string.isRequired,
year: PropTypes.number.isRequired,
genres: PropTypes.arrayOf(PropTypes.string).isRequired
};
export default Movie;
import React from 'react';
function Detail(props){
console.log(props);
return <h1>Hello!</h1>;
}
export default Detail;
import React from 'react';
class Detail extends React.Component {
componentDidMount(){
console.log(this.props);
const {location, history} = this.props;
if (location.state === undefined){
history.push("/");
}
}
render(){
const {location} = this.props;
if (location.state)
{
return <span>{location.state.title}</span>;
}
else {
return null;
}
}
}
export default Detail;