만들면서 배우는 리액트 튜토리얼 - 5.State
State는 리액트 컴포넌트 안에 있는 객체이다.
중요한 것은 State가 바뀔 때마다, 새로운 state와 함께 다시 render()가 실행된다는 것이다.
import React, { Component } from 'react';
import './App.css';
import Movie from './Movie'
const movies = [
{
title:"Matrix",
poster:"https://images-na.ssl-images-amazon.com/images/I/51vpnbwFHrL._SY445_.jpg",
},
{
title:"Full Metal Jacket",
poster:"https://i.pinimg.com/originals/36/1e/cd/361ecdb85a3767f70810cbe2cdaaf1a4.jpg",
},
{
title:"Oldboy",
poster:"https://m.media-amazon.com/images/M/MV5BYjQwZTc3ODktZjk1ZS00N2Y3LWFlYmUtMmQ4M2IwMjZlYTIwXkEyXkFqcGdeQXVyNjQwMzk1MDM@._V1_.jpg",
},
{
title:"Star wars",
poster:"http://cdn.shopify.com/s/files/1/0151/0741/products/Star_Wars_Portrait-NGPS1263_Copy_1024x1024.jpg?v=1504676565",
},
]
class App extends Component {
state = {
greeting: 'Hello'
}
// 5초 뒤 state에 greeting을 Hello again!으로 바꿔라!
componentDidMount() {
setTimeout(() => {
this.setState({ // this.state.greeting을 직접 바꾸면 안됨
greeting: 'Hello again!'
})
}, 5000)
}
render() {
return (
<div className="App">
{this.state.greeting}
{/* movies.map을 하면 movie에 movies의 객체가 하나씩 들어가고 {}안에 것을 리턴한다 */}
{movies.map((movie, index) =>
<Movie title={movie.title} poster={movie.poster} key={index} />
)}
</div>
);
}
}
export default App;
state 기본 예시
1.1. State 활용
import React, { Component } from 'react';
import './App.css';
import Movie from './Movie'
class App extends Component {
state = {
movies: [
{
title:"Matrix",
poster:"https://images-na.ssl-images-amazon.com/images/I/51vpnbwFHrL._SY445_.jpg",
},
{
title:"Full Metal Jacket",
poster:"https://i.pinimg.com/originals/36/1e/cd/361ecdb85a3767f70810cbe2cdaaf1a4.jpg",
},
{
title:"Oldboy",
poster:"https://m.media-amazon.com/images/M/MV5BYjQwZTc3ODktZjk1ZS00N2Y3LWFlYmUtMmQ4M2IwMjZlYTIwXkEyXkFqcGdeQXVyNjQwMzk1MDM@._V1_.jpg",
},
{
title:"Star wars",
poster:"http://cdn.shopify.com/s/files/1/0151/0741/products/Star_Wars_Portrait-NGPS1263_Copy_1024x1024.jpg?v=1504676565",
},
]
}
componentDidMount() {
// 5초 이후에 실행
setTimeout(() =>
this.setState({
movies: [
...this.state.movies, // 이전 state를 그대로 두고
// 아래 내용을 추가해라
{
title:"transporting",
poster:"https://upload.wikimedia.org/wikipedia/en/thumb/7/71/Trainspotting_ver2.jpg/250px-Trainspotting_ver2.jpg",
}
]
}), 5000)
}
render() {
return (
<div className="App">
{/* movies.map을 하면 movie에 movies의 객체가 하나씩 들어가고 {}안에 것을 리턴한다 */}
{this.state.movies.map((movie, index) =>
<Movie title={movie.title} poster={movie.poster} key={index} />
)}
</div>
);
}
}
export default App;
state 활용 예시
위의 코드는 기존 const 배열로 선언했던 movies를 state로 바꾸고 setTimeout을 사용하여 5초
이후에 새로운 영화를 기존 목록에 추가하는 코드이다. 배열 데이터가 state로 바뀌었기
때문에 render()에서도 this.state.movies.map으로 사용하며, 고유한 key가 필요하다는
warning을 제거하기 위해 key prop으로 map에서 자동으로 넘어오는 index를 넣어준다.
componentDidMount() {
// 5초 이후에 실행
setTimeout(() =>
this.setState({
movies: [
...this.state.movies, // 이전 state를 그대로 두고
// 아래 내용을 추가해라
{
title:"trainspotting",
poster:"https://upload.wikimedia.org/wikipedia/en/thumb/7/71/Trainspotting_ver2.jpg/250px-Trainspotting_ver2.jpg",
}
]
}), 5000)
}
componentDidMout를 사용하여 컴포넌트가 완전히 마운트 된 후에 setTimeout(fn, 5000)을
사용하여 5초 뒤에 함수가 실행되게 한다. …this.state.movies는 기존 state 목록을 불러온다.
즉, 처음에는 기존 영화 리스트가 출력되고 5초 뒤에 기존 목록 밑에
다른 영화(trainspotting)가 추가 되는 것이다.
1.2. Loading States(state를 이용한 API call 구현)
import React, { Component } from 'react';
import './App.css';
import Movie from './Movie'
class App extends Component {
state = {
}
componentDidMount() {
// 5초 이후에 실행
setTimeout(() =>
this.setState({
movies: [
{
title:"transporting",
poster:"https://upload.wikimedia.org/wikipedia/en/thumb/7/71/Trainspotting_ver2.jpg/250px-Trainspotting_ver2.jpg",
},
{
title:"Matrix",
poster:"https://images-na.ssl-images-amazon.com/images/I/51vpnbwFHrL._SY445_.jpg",
},
{
title:"Full Metal Jacket",
poster:"https://i.pinimg.com/originals/36/1e/cd/361ecdb85a3767f70810cbe2cdaaf1a4.jpg",
},
{
title:"Oldboy",
poster:"https://m.media-amazon.com/images/M/MV5BYjQwZTc3ODktZjk1ZS00N2Y3LWFlYmUtMmQ4M2IwMjZlYTIwXkEyXkFqcGdeQXVyNjQwMzk1MDM@._V1_.jpg",
},
{
title:"Star wars",
poster:"http://cdn.shopify.com/s/files/1/0151/0741/products/Star_Wars_Portrait-NGPS1263_Copy_1024x1024.jpg?v=1504676565",
},
]
}), 5000)
}
_renderMovies = () => {
const movies = this.state.movies.map((movie, index) =>
<Movie title={movie.title} poster={movie.poster} key={index} />
)
return movies
}
render() {
return (
<div className="App">
{/*
this.state.movies 데이터가 존재하면 기존 state mapping을 실행하고,
아니면 Loading... string을 띄워준다
*/}
{this.state.movies ? this._renderMovies() : "Loading..."}
</div>
);
}
}
export default App;
Loading State 구현 예시
실제 동작하는 프로그램에서는 처음부터 데이터가 존재하기 보다는 주로 API를 통해서 데이터를 불러온다.
위의 코드는 이 로딩 시간동안 Loading이라고 띄워주는 패턴의 예시이다.
우선 state를 빈 배열로 두고, componentDidMount()와 setTimeout()을 이용하여 컴포넌트가
다 마운트 되고 5초 뒤 state에 데이터를 추가해준다. 이렇게 하고 바로 this.state.movies를
render해주면 처음에 state가 비어있기 때문에 5초 뒤 데이터가 추가되기 전에 this.state가
존재하지 않는다는 에러가 발생한다.
그래서 _renderMovies()라는 함수를 정의해서 기존 mapping 코드를 변수 형태로 만들고
이 변수 값을 return해준다. 그리고 실제 render()에서는 3항 연산자(조건 ? 참 : 거짓)를
이용하여 데이터가 존재하면 _renderMovies()를 실행하고 데이터가 아직 없을 경우
Loading…이라는 string을 출력 해준다.
1.3. Smart vs Dumb Component(class vs function)
굳이 state나 componentDidMount() 같은 복잡한 기능들이 아니라 prop만 필요한 단순한
컴포넌트가 있다. Movie App에서 보면 기존에 구현한 MoviePoster 클래스가 그렇다.
Img 태그 하나를 리턴 할 뿐 다른 기능이 없기 때문에 굳이 class로 만들 필요가 없다.
이럴 때는 function으로 바꾸어서 구현하면 복잡한 기능을 빼고 심플하게 사용할 수 있다.
/*
class MoviePoster extends Component {
static propTypes = {
poster: PropTypes.string.isRequired,
}
render() {
return(
<img src={this.props.poster} />
)
}
}
*/
function MoviePoster({poster}) {
return(
<img src={poster} alt="Movie Poster" />
)
}
이 예제 코드에서 아래 function과 주석처리된 위의 class는 같은 기능을 수행한다.
0 comments