다음 코드는 렌더링된 출력값을 변경하기위해 ReactDOM.render()을 호출한것이다.
function tick() {
const element = (
<h1>Hello,world</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
);
ReactDOM.render(
element,
document.getElementByid('root')
);
}
setInterval(tick, 1000);
아래의 예제는 컴포넌트를 재사용하고, 캡슐화 하는 코드이다. 이 컴포넌트는 스스로 타이머를 설정하고, 매초 업데이트 한다.
function Clock(){
return (
<div>
<h1>Hello,world</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
)
}
function tick() {
ReactDOM.render(
<Clock date={new Date()} />,
document.getElementById('root')
);
}
setInterval(tick, 1000);
위의 Clock()
과 같은 함수 컴포넌트를 다섯단계의 클래스로 변환할수 있다.
1. `React.Component`를 확장하는 동일한 이름의 ES6 `class` 생성
2. `render()` 메서드 추가
3. 함수의 내용을 `render()` 메서드 안으로 옮긴다.
4. `render()` 내용 안에있는 `props`를 **`this.props`** 로 변경한다.
5. 남아 있는 빈 함수 선언을 삭제한다.
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
render 메서드는 업데이트가 발생할 때마다 호출되지만, 같은 DOM노드로 을 렌더링 하는 경우 Clock
클래스의 단일 인스턴트만 사용된다. 이것은 로컬 state와 생명주기 메서드와 같은 부가적인 기능을 사용할 수 있게 해준다.
class Clock extends React.Component {
// 2. this.state를 지정하는 class constructor를 추가한다.
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
// 1.this.props.data => this.state.data로 변경한다.
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
props
를 기본 constructor에 전달하는 방법
constructor(props) {
super(props);
this.state = { date: new Date() }
}
클래스 컴포넌트는 항상
props
를 기본으로 constructor를 호출해야 한다.
최종적인 결과는 다음과 같다
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
많은 컴포넌트가 있는 애플리케이션에서 컴포넌트가 삭제될 때 해당 컴포넌트가 사용중이던 리소스를 확보하는 것은 중요하다!!
Clock이 처음 DOM에 렌더링 될 때마다 타이머를 설정하려고 하는데 이것을 React에서는 "마운팅" 이라고 한다. 또한 Clock에 의해 생성된 DOM이 삭제될 때마다 타이머를 해제 하려고 한다.
컴포넌트 클래스에서 특별한 메서드를 선언하여 컴포넌트가 마운트되거나 언마운트 될 때 일부 코드를 작동할 수 있다.
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
componentDidMount()
메서드는 컴포넌트 출력물이 DOM에 렌더링 된 후에 실행됨.
tick()
메소드는 컴포넌트 로컬 state를 업데이트 하기 위해 this.setState()
를 사용한다.
setState()
state값을 변경해준후 업데이트 해준다.
// 렌더링하지 않는다.
this.state.comment = 'Hello';
setState()를 사용하자!!
// 렌더링 한다.
this.setState({comment: 'Hello'})
this.state
를 지정할수 있는 유일한 공간은 constructor이다.
다음 예제에서 this.props와 this.state가 비동기적으로 업데이트 될수 있기 때문에 다음 state를 계산할 때 해당 값에 의존하면 안된다
//Wrong
this.setState({
counter: this.state.counter + this.props.increment,
})
보다더 좋은 방법은 객체보다는 함수를 인자로 사용하는 다른 형태의 setState()
를 사용한다. 그 함수는 이전 state를 첫번째 인자로 받아 들일 것이고, 업데이트가 적용된 시점의 props를 두번째 인자로 받아들일것이다.
this.setState((state,props) => ({
counter: state.counter + props.increment
}))
setState()
를 호출할 때 현재 state로 병합한다.
예를 들어, state는 다양한 독립적인 변수를 포함할 수 있다.
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
별도의 setState() 호출로 이러한 변수를 독립적으로 업데이트 할 수 있다.
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
보모 컴포넌트나 자식 컴포넌트 모두 특정 컴포넌트가 유/무 상태인지 알수 없고, 함수나 클래스로 정의 되었는지에 대해서 관심을 가질 필요가 있다.
이러한 이슈로 state는 로컬 또는 캡슐화라고 불리운다. state가 소유하고 설정한 컴포넌트 이외에는 어떠한 컴포넌트에도 접근할 수 없다.
컴포넌트는 자신의 state를 자식 컴포넌트에 props로 전달 할수 있다.
<FormattedDate date={this.state.date} />
FormattedDate
컴포넌트는 date를 자신의 props로 받을 것이고 이것이 Clock의 state로부터 왔는지, Clock의 props에서 왔는지, 수동으로 입력한 것인지 알지 못한다.
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}
일반적으로 이를 하향식 또는 단방향식 데이터 흐름이라고 한다. 모든 state는 항상 특정한 컴포넌트가 소유하고 있으며 그 state로 부터 파생된 UI또는 데이터는 오직 트리구조에서 자신의 아래에 있는 컴포넌트에만 영향을 미친다.
참고
해당 포스팅은 실전 리엑트 공식 홈페이지 https://ko.reactjs.org/
리엑트 자습서 https://ko.reactjs.org/docs/state-and-lifecycle.html 에서 참고하여 작성했습니다.