🔎 React 공식문서 자료
React_State and Lifecycle
⚙️ 예시 코드 - 1
ReactDOM.render()
를 호출function 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);
⚙️ 예시 코드 - 2
Clock
컴포넌트를 완전히 재사용하고 캡슐화function Clock(props) {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {props.date.toLocaleTimeString()}.</h2>
</div>
);
}
function tick() {
ReactDOM.render(
<Clock date={new Date()} />,
document.getElementById('root')
);
}
setInterval(tick, 1000);
⚙️ 예시 코드 - 3
예시코드 - 2
에는 중요한 요건이 누락되어 있다. Clock
이 타이머를 설정하고 매초 UI를 업데이트하는 것이 Clock
의 구면 세부사항이 되여야 한다.Clock
이 스스로 업데이트 업데이트하도록 만들려면 state
를 추가해야 한다.state
는 props
와 유사하지만, 비공개이며 컴포넌트에 위해 완전히 제어된다.ReactDOM.render(
<Clock />,
document.getElementById('root')
);
✨ 함수 컴포넌트 ▶ 클래스 컴포넌트 변환 과정
1. React.Component
를 확장하는 동일한 이름의 ES6 class를 생성
2. render()
라고 불리는 빈 메서드 추가
3. 함수의 내용을 render()
메서드 안으로 옮김
4. render()
내용 안에 있는 props
를 this.props
로 변경
5. 남아있는 빈 함수 선언을 삭제
⚙️ 클래스 컴포넌트로 정의된 코드
Clock
은 클래스 컴포넌트로 정의!!render
메서드는 업데이트가 발생할 때마다 호출되지만, 같은 DOM노드로 <Click />
을 렌더링하는 경우 Clock
클래스의 단일 인스턴스만 사용된다. 이것은 로컬 state와 생명주기 메서드와 같은 부가적인 기능ㄹ 사용할 수 있게 해준다.class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
✨ props ▶ state 변환 과정
1. render()
메서드 안에 있는 this.props.date
를 this.state.date
로 변경
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
// props ▶ state
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
this.state
를 지정하는 class constructor
를 추가// class 키워드 추가
class Clock extends React.Component {
// 🔎 constructor
// - class로 생성된 객체를 생성하고 초기화하기 위한 특수한 메서드
// - class 안에 단 1개만 존재 ▶ 여러 개가 존재하면 문법오류(SyntaxError)
// - 부모 클래스의 constructor를 호출하기 위해 super 키워드를 사용
// 🛑 클래스 컴포넌트는 항상 props로 기본 constructor를 호출
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>
);
}
}
<Clock />
요소에서 date
props을 삭제ReactDOM.render(
<Clock />,
document.getElementById('root')
);
⚙️ 완성 코드
clock
이 스스로 타이머를 설정하고 매초 스스로 업데이트하도록 코드를 수정많은 컴포넌트가 있는 애플리케이션에서 컴포넌트가 삭제될 때 해당 컴포넌트가 사용 중이던 리소스를 확보하는 것이 중요하다.
⚙️ 생명주기 메서드
Clock
이 처음 DOM에 렌더링 될 때마다 타이머를 설정 ▶ 마운팅(Mountimg)Clock
에 의해 생성된 DOM이 삭제될 때마다 타이머를 해제 ▶ 언마운팅(Unmounting)class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
}
componentWillUnmount() {
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
✅ ComponentDidMount()
// 타이머 설정
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
✅ componentWillUnmount()
componentWillUnmount() {
clearInterval(this.timerID);
}
this.timerID
사용을 주의해야한다.
this.props
가 React에 의해 스스로 설정되고 this.state
가 특수한 의미가 있지만, 타이머 ID와 같이 데이터 흐름안에 포함되지 않는 어떤 항목을 보관할 필요가 있다면 자유롭게 클래스에 수동으로 부가적으로 필드를 추가해도 된다.
⚙️ 최종 코드
this.setState()
를 사용✨ 최종 코드 : 호출 순서
<Clock />
가 ReactDOM.render()
로 전달되었을 때 React는 Clock
컴포넌트의 constructor를 호출 → Clock
이 현재 시각을 표시해야 하기 때문에 현재 시각이 포함된 객체로 this.state
를 초기화 & 나중에 state를 업데이트
React Clock
컴포넌트의 render()
메서드 호출 → React는 화면에 표시되어야 할 내용을 확인 → React는 Clock
의 렌더링 출력값을 일치시키기 위해 DOM 업데이트
Clock
출력값이 DOM에 삽입 → React는 componentDidMount()
생명 주기 메서드 호출 → Clock
컴포넌트는 매초 컴포넌트의 tick()
메서드를 호출하기 위한 타이머를 설정하도록 브라우저 요청
매초 브라우저가 tick()
메서드를 호출 → Clock
컴포넌트는 setState()
에 현재 시각을 포함하는 객체를 호출 & UI 업데이트 → setState()
호출 덕분에 React는 변경을 감지 & 화면에 표시될 내용을 알아내기 위해 render()
메서드를 재호출 → render()
메서드 안의 this.state.date
가 달라지고 렌더링 출력값은 업데이트된 시작을 포함 → React는 이에 따라 DOM을 업데이트
Clock
컴포넌트가 DOM으로부터 한 번이라도 삭제된 적이 딨으면 React는 타이머를 멈추기 위해 componentWillUnmount()
생명주기 메서드 호출
setState()
를 사용하기 위해 알아야할 세 가지가 있다.
✨ 직접 State를 수정하지 않는다.
// Wrong ▶ 컴포넌트를 다시 렌더링 하지 않음
this.state.comment = 'Hello';
// Correct ▶ this.state를 지정할 수 있는 유일한 공간은 constructor
this.setState({comment: 'Hello'});
✨ state 업데이트는 비동기적일 수도 있다.
React는 성능을 위해 여러 setState()
호출을 단일 업데이트로 한꺼번에 처리할 수 있다.
this.props
와 this.state
가 비동기적으로 업데이트될 수 있기 때문에 다음 state를 계산할 때 해당 값에 의존해서는 안 된다.
다음 코드는 카운터 업데이트에 실패한 경우이다.
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
위 코드를 수정하기 위해 객체보다는 함수를 인자로 사용하는 다른 형태의 setState()
를 사용해야 한다. 그 함수는 이전 state를 첫 번째 인자로 받아들일 것이고, 업데이트가 적용된 시점의 props를 두 번째 인자로 받아들일 것이다
// ▶ 화살표 함수 (arrow function)
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
// ▶ 일반 함수 (function)
// Correct
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});
✨ state 업데이트는 병합된다.
setState()
를 호출할 때 React는 제공한 객체를 현재 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
});
});
}
병합은 얕게 이루어지기 때문에 this.setState({comments})
는 this.state.posts
에 영향을 주진 않지만 this.state.comments
는 새로운 값으로 대체된다.
✨ 데이터는 아래로 흐른다.
부모 컴포넌트나 자식 컴포넌트 모두 측정 컴포넌트가 유 상태인지 또는 무 상태인지 알수 없고, 그들이 함수나 클래스로 정의되었는지에 대해 관심을 가질 필요가 없다.
이 때문에 state는 종종 로컬 또는 캡슐화라고 불린다. state가 소유하고 설정한 컴포넌트 이외에는 어떠한 컴포넌트에도 접근할 수 없다.
컴포넌트는 자신의 state를 자식 컴포넌트에 props로 전달할 수 있다.
<FormattedDate date={this.state.date} />
FormatteedDate
컴포넌트는 date
를 자신의 props로 받을 것이고 이것이 Clock
의 state로부터 왔는지, Clock
의 props에서 왔는지, 수통으로 입력한 것인지 알지 못한다.
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}
일반적으로 이를 하향식(top-dwon) 또는 단방향식 데이터 프흠이라고 한다. 모든 state는 항상 특정한 컴포넌트가 소유하고 있으며 그 state로 부터 파생된 UI 또는 데이터는 오직 트리구조에서 자신의 아래에 있는 컴포넌트에만 영향을 미친다.
트리구조가 props들의 폭포라고 상상하면 각 컴포넌트의 state는 임의의 점에서 만나지만 동시에 아래로 흐르는 부가적인 수원(water source)이라고 할 수 있다.
모든 컴포넌트가 완전히 독립적이라는 것을 보여주기 위해 App
렌더링 하는 세 개의 <Clock>
예시 코드이다.
function App() {
return (
<div>
<Clock />
<Clock />
<Clock />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
각 Clock
은 자신만의 타이머를 설정하고 독립적으로 업데이트를 한다.
React 앱에서 컴포넌트가 유상태 또는 무상태에 대한 것은 시간이 지남에 따라 변경될 수 있는 구현 세부 사항으로 간주한다. 유상태 컴포넌트 안에서 무상태 컴포넌트를 사용할 수 있으며, 그 반대 경우도 마찬가지로 사용할 수 있다.