[react] state와 생명주기
funciton tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(
element,
document.getElementById('root')
);
}
setInterval(tick, 1000);
Clock 컴포넌트를 완전히 재사용하고 캡슐화하는 방법은 뭘까?
이 컴포넌트는 스스로 타이머를 설정하며, 매초 스스로 업데이트 할 것이다
function Clock(props) {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {props.date.toLocaleTimeString()}.</h2>
</div>
);
}
funciton tick() {
ReactDOM.render(
<Clock date={new Date()} />,//-->Clock props로 date를 넘긴다
document.getElementById('root')
);
}
setInterval(tick, 1000);
function Clock(props) {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {props.date.toLocaleTimeString()}.</h2>
</div>
);
}
funciton tick() {
ReactDOM.render(
<Clock date={new Date()} />,//-->Clock props로 date를 넘긴다
document.getElementById('root')
);
}
setInterval(tick, 1000);
class Clock extends React.Component {//클래스로 변환한 Clock 컴포넌트
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};//초기 this.state를 지정하는 class constructor을 추가한다
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
)
생명주기 메서드를 클래스에 추가하기
Clock이 처음 DOM에 렌더링 될때마다 타이머를 설정하려고 하는 것 --> 마운팅
Clock에 의해 생성된 DOM이 삭제될 때마다 타이머를 해제하려고 하는 것 --> 언마운팅
컴포넌트 클래스에서는 특별한 메서드를 선언하여 컴포넌트가 마운트되거나 언마운트 될때 일부 코드를 작동 할 수 있다
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};//2. 현재시각을 표시해야 하기떄문에 현재시간이 포함된 객체로 this.state를 초기화한다
}
componentDidMount() { //-> 컴포넌트 출력물이 DOM에 렌더링 된 후에 실행 //타이머를 설정하기에 좋은 장소
this.timerID = setInterval(//4. Clock 출력값이 DOM에 삽입되면, 호출 매초 컴포넌트의 tick() 메서드를 호출하기 위한 타이머를 설정하도록 브라우저에 요청
() => this.tick(),
1000
);
}
componentWillUnmount() {//타이머를 분해하는 메서드
clearInterval(this.timerID)
}
tick() {//5. 매초 브라우저가 tick() 메서드를 호출하고 (아래에 작성)
this.setState({
//setState()에 현재 시각을 포함하는 객체를 호출하면서 ui업데이트를 진행,
//setState() 호출덕분에 react는 state가 변경된 것을 인지함,
//화면에 표시된 내용을 알아내기 위해 render() 메서드를 다시 호출한다
//이때 render() 메서드 안의 this.state.date가 달라지고 렌더링 출력값은 업데이트되니 시각을 포함함
date: new Date()
});
}
render() { //3. render을 호출하여 화면에 표시되어야 할 내용을 알게 됨 그 다음 Clock의 렌더링 출력값을 일치시키기위해 DOM을 업데이트 함
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
)
}
}
ReactDOM.render(
<Clock />,//1. Clock 컴포넌트의 constructor을 호출
document.getElementById('root')
);
State를 올바르게 사용하기
1. 직접 State를 수정하지 말기
this.state.comment = 'Hello';
//이 코드는 컴포넌트를 다시 렌더링 하지 않는다
this.setState({comment: 'Hello'});
//setState()를 사용하여 렌더링을 할 수 있다
//this.state를 지정할 수 있는 유일한 공간은 constructor이다
State 업데이트는 비동기적일 수 있다
React는 성능을 위해 여러 setState() 호출을 단일 업데이트로 한꺼번에 처리할 수 있다
this.props와 this.state가 비동기적으로 업데이트 될 수 있기 때무에 다음 state를 계산할 때에는 해당 값에 의존해서는 안된다
this.setState({
counter: this.state.counter + this.props.increment,
});
//이 코드는 카운터 업데이트에 실패할 수 있다
이를 수정하기 위해 객체보다는 함수를 인자로 사용하는 다른 형태의 setState()를 사용한다
this.setState((state, props) => {//state: 이전 state , props: 업데이트가 적용된 시점
counter: state.counter + props.increment
})// 이전 state를 첫번째 인자로 받아들일 것이고, 업데이트가 적용된 시점의 props를 두번째 인자로 받아들인다
this.setState(function(state, props) {//화살표함수가 아니어도 일반함수에서 정상적으로 작동함
return (
counter: state.counter + props.increment
)
})
3. State 업데이트는 병합된다
setState()를 호출할 때 React는 제공한 객체를 현재 state로 병합한다
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []//state는 다양한 독립적인 변수를 포함한다
}
}
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
})
})
//별도의 setState() 호출로 이러한 변수를 독립적으로 업데이트가 가능하다
}
병합은 얕게 이루어지기 때문에
this.setState({comments})는 this.state.posts에 영향을 주진 않지만
this.state.comments는 완전히 대체됩니다.(뭔소리지..?)
데이터는 아래로 흐른다
부모 컴포넌트나 자식 컴포넌트 모두 특정 컴포넌트가 유상태인지 또는 무상태인지 알 수 없다
그들이 함수나 클래스로 정의되었는지에 대해서는 관심을 가질 필요가 없다
이 때문에 state는 종종 로컬 또는 캡슐화로 부른다
state가 소유하고 설정한 컴포넌트 이외에는 어떠한 컴포넌트에도 접근할 수 없다
컴포넌트는 자신의 state를 자식 컴포넌트에 props로 전달 할 수 있다
<FormattedDate date={this.state.date} />
//FormattedDate 컴포넌트는 date를 자신의 props로 받을 것인지,
//이것을 Clock의 state로부터 왔는지,
//Clock의 props에서 왔는지,
//수동으로 입력한 것인지 알지 못한다
일반적으로 이를 하향식, 단방향식 데이터 흐름이라고 한다
모든 state는 항상 특정한 컴포넌트가 소유하고 있고,
그 state로부터 파생된 ui 또는 데이터는 오직 트리구조에서 자신의 아래에 있는 컴포넌트에만 영향을 미침
트리구조 : props들의 폭포
각컴포넌트의 state: 임의의점에서 만나지만 동시에 아래로 흐르는 부가적인 수원(water source)
function App() {
return (
<div>
<Clock />//각 Clock은 자신만의 타이머를 설정하고 독립적으로 업데이트를 한다
<Clock />
<Clock />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
React 앱에서 컴포넌트가 유상태 또는 무상태에 대한 것은 시간이 지남에 따라 변경될 수 있는 구현 세부 사항으로 간주합니다. 유상태 컴포넌트 안에서 무상태 컴포넌트를 사용할 수 있으며, 그 반대 경우도 마찬가지로 사용할 수 있습니다.(뭔소리지2)