이번에 React의 State와 Lifecycle에 대해 알아보겠습니다.
state
const root = ReactDOM.createRoot(document.getElementById('root'));
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
root.render(element);
}
setInterval(tick, 1000);
위의 코드를 완전히 재사용하고 Clock Component로 캡슐화하겠습니다.
const root = ReactDOM.createRoot(document.getElementById('root'));
function Clock(props) {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {props.date.toLocaleTimeString()}.</h2>
</div>
);
}
function tick() {
root.render(<Clock date={new Date()} />);
}
setInterval(tick, 1000);
위의 코드에서는 중요한 요건이 누락되었습니다. Clock이 타이머를 설정하고 매초 UI를 업데이트하는 것이 Clock의 구현 세부사항이 되어야 하고, 이상적으로 한 번만 코드를 작성하고 Clock이 스스로 업데이트하도록 하려면 Clock Component에 state를 추가해야 합니다. state는 props와 유사하지만, 비공개이며 Component에 의해 완전히 제어됩니다. 이를 위해서 먼저 Clock을 함수에서 클래스로 변환합니다.
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
render 메서드는 업데이트가 발생할 때마다 호출되지만, 같은 DOM 노드로 <Clock />을 렌더링하는 경우 Clock 클래스의 단일 인스턴스만 사용됩니다. 이것은 로컬 state와 생명주기 메서드와 같은 부가적인 기능을 사용할 수 있게 합니다. 다음으로 이 클래스에 state를 추가합니다.
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>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Clock />);
this.props.date를 this.state.date로 변경하고, 초기 this.state를 지정하는 class constructor를 추가합니다.
lifecycle
많은 Component가 있는 애플리케이션에서 Component가 삭제될 때 해당 Component가 사용 중이던 리소스를 확보하는 것이 중요합니다. Clock이 처음 DOM에 렌더링 될 때마다 타이머를 설정하는 것을 마운팅, 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>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Clock />);
위의 코드의 진행 순서를 요약해보면 먼저 <Clock />가 root.render()로 전달되었을 때 React는 Clock Component의 constructor를 호출합니다. Clock이 현재 시각을 표시해야 하기 때문에 현재 시각이 포함된 객체로 this.state를 초기화합니다. 다음으로 React는 Clock 컴포넌트의 render() 메서드를 호출합니다. 이를 통해 React는 화면에 표시되어야 할 내용을 알게 되고 Clock의 렌더링 출력값을 일치시키기 위해 DOM을 업데이트합니다. Clock 출력값이 DOM에 삽입되면, React는 componentDidMount() 생명주기 메서드를 호출합니다. 그 안에서 Clock Component는 매초 Component의 tick() 메서드를 호출하기 위한 타이머를 설정하도록 브라우저에 요청합니다. 그러면 매초 브라우저가 tick() 메서드를 호출히고 Clock Component는 setState()에 현재 시각을 포함하는 객체를 호출하면서 UI 업데이트를 진행합니다. setState() 호출 덕분에 React는 state가 변경된 것을 인지하고 화면에 표시될 내용을 알아내기 위해 render() 메서드를 다시 호출합니다. 이 때 render() 메서드 안의 this.state.date가 달라지고 렌더링 출력값은 업데이트된 시각을 포함하고 React는 이에 따라 DOM을 업데이트합니다. 마지막으로 Clock Component가 DOM으로부터 한 번이라도 삭제된 적이 있다면 React는 타이머를 멈추기 위해 componentWillUnmount() 생명주기 메서드를 호출합니다.
※ setState() 주의사항
1. State를 직접 수정하면 안됩니다. 예를 들어, this.state.comment = 'Hello'; 와 같은 코드는 Component를 다시 렌더링하지 않습니다.
2. State 업데이트는 비동기적일 수 있습니다.
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
// 일반적인 함수도 가능
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});
3. State 업데이트는 병합됩니다. 예를 들어, state는 다양한 독립적인 변수를 포함할 수 있고, 별도의 setState() 호출로 이러한 변수를 독립적으로 업데이트할 수 있습니다.
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
모든 state는 항상 특정한 Component가 소유하고 있으며 그 state로부터 파생된 UI 또는 데이터는 오직 트리구조에서 자신의 아래에 있는 Component에만 영향을 미칩니다. 일반적으로 이를 하향식(top-down) 또는 단방향식 데이터 흐름이라고 합니다. 트리구조가 props들의 폭포라고 상상하면 각 Component의 state는 임의의 점에서 만나지만 동시에 아래로 흐르는 부가적인 수원이라고 할 수 있습니다.
'React' 카테고리의 다른 글
리스트(List), 키(Key), 폼(Form) (0) | 2022.11.10 |
---|---|
이벤트 처리와 조건부 렌더링 (0) | 2022.11.09 |
Element Rendering, Component, Props (0) | 2022.11.08 |
JSX란? (0) | 2022.11.08 |
React (0) | 2022.07.31 |