State란?
React 컴포넌트에서 다루는 데이터는 Props와 State 두 가지로 나뉜다.
Props는 부모 컴포넌트가 자식 컴포넌트에 값을 전달할 때 사용하는 것으로 "읽기 전용"이다. 자식 컴포넌트는 값을 받기만 하고 직접 수정할 수 없다.
이 점을 이용해서 변경되지 말아야 할 데이터를 효율적으로 관리할 수 있다.
State는 컴포넌트 자기 자신이 갖고 있는 값이다. 내부에서 선언하고 내부에서 변경할 수 있다. props와 비교해서 "쓰기 전용"이라고 볼 수 있다.
React doc 예시
React 공홈에 나와있는 문서 내용을 따라가보자!
3. 엘리먼트 렌더링에서 만든 시계 예제를 살펴보자.
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);
setInterval 함수를 이용해서 1000m/s초마다 tick 함수를 실행시키고, tick함수는 ReactDOM.render를 실행시켜 화면을 계속 바꿔준다.
React 앱은 ReactDOM.render()를 한번만 호출하는데 보이는 코드는 setInterval을 통해 1000m/s마다 ReactDOM.render()를 호출한다.
굳이 setInterval을 써? 그럼 변화 있을 때 마다 setInterval설정해줄래? 우리는 컴포넌트 스스로 타이머를 설정하고, 매 초 스스로 업데이트 하도록 만들어줄 거다.
이점은? 1. 컴포넌트 내부에서 완전한 제어가 가능하다 → 코드가 깔끔해짐 2. 캡슐화(숨김)가 가능하다. 3. 사용하는 쪽과 구현하는 쪽을 철저하게 분리시켜 양쪽의 편의성을 높인다. → 기능 분리, 코드 깔끔
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);
구현하기 전에 코드를 좀 더 깔끔하게 만들었다. 시계 기능을 하는 함수 Clock과 렌더링해주는 tick함수로 나눴다.
함수에서 클래스 변환하기
State는 class 컴포넌트만 가질 수 있다. 그래서 우리는 해당 코드를 함수에서 클래스로 바꿔준다. (그래서 함수형 컴포넌트를 stateless component라고 부르기도 한다)
과정은 다음과 같다.
- React.Component를 extends하는 class 생성
- render() 메서드를 추가
- clock() 내용을 render() 메서드에 옮기고
- props를 this.props로 바꾼다. ( 해당 class 내의 props임을 밝혀줘야함)
- clock() 필요없는 거 정리
class Clock extends React.Component{ render(){ <div> <h1> Hello, World! </h1> <h2> It is {this.props.date.toLocaleTimeString().</h2> </div> } } function tick(){ ReactDOM.render( <Clock date={new Date()} />, document.getElementById('root') ); } setInterval(tick, 1000);
Clock은 이제 클래스입니다. 로컬 state와 생명 주기 메서드와 같은 부가적인 기능을 사용할 수 있다.
클래스에 로컬 state 추가하기
로컬state 쓰려면 추가해야겠지? props빼고 state로 바꿔줘야겠지?
- render() 메서드 안에 있는 this.props.date를 this.state.date 변경해준다.
- 초기 값을 지정하는 class constructor 추가한다. 현재 값도 초기화 this.state = ;
- <Clock /> 요소에서 date props를 삭제한다. 제대로 분리해주자.
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.toLocalTimeString()}.</h2> </div> ) }; } ReactDOM.render( <Clock />, document.getElementById('root') );
생명주기 메서드 추가
애플리케이션에서 컴포넌트가 많으면 많을수록 삭제 된 컴포넌트가 쓰던 리소스를 확보하는 게 중요하다.
우리는 지금 <Clock/>이 DOM에 렌더링 될 때 마다 타이머를 설정하려고 한다. 이를 "마운팅"이라고 부른다. DOM이 삭제 될 때 타이머를 해제하려고 하는데 이를 "언마운팅"이라 부른다.
컴포넌트가 마운트되거나 언마운트 될 때 특정 코드를 작동시킬 수 있다.
컴포너트 출력물이 DOM에 Render될 때 componentDidMount() 사용 → 타이머 설정하는 데 사용하면 적절할 듯 하다.
컴포넌트가 제거되기 직전에 무언가 실행하고자 한다면? componentWillUnmount() 사용
→ 타이머 해제하는데 적절할 듯 하다.
이런 메서드를 "생명주기 메서드"라고 부른다. 주기... 시작과 끝이 있는....
componentDidMount(){ this.timerID = setInterval( () => this.tick(), 1000 ); } componentWillUnmount(){ clearInterval(this.timerID); }
마지막으로 Clock 컴포넌트가 매초 작동하도록 tick()이란 메서드를 구현했다. ★state 업데이트는 반드시 this.setState()를 사용한다.
tick(){ this.setState({ date: new Date() }); }
최종 코드
class Clock extends React.Componet{ 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.toLocalTimeString()}.</h2> </div> ); } } ReacteDOM.render( <Clock />, document.getElementById('root') );
요약:
- <Clock />가 ReactDOM.render()로 전달되었을 때 React는 Clock 컴포넌트의 constructor를 호출한다. Clock이 현재 시각을 표시해야 하기 때문에 현재 시각이 포함된 객체로 this.state를 초기화 한다. (나중에 이 state가 업데이트 됨)
- React는 Clock 컴포넌트의 render() 메서드를 호출한다. 이를 통해 React는 화면에 표시할 내용을 알게 된다. 어? 출력값이 달라졌네? React는 Clock의 렌더링 출력값을 일치시키기 위해 DOM을 업데이트 한다.
- Clock 출력값이 DOM에 삽입되면 React는 componetDidMount() 생명주기 메서드를 호출한다. (DOM 생성 될 때니까) 그럼 내부에 tick() 메서드를 호출하기 위한 타이머를 설정하라고 요구한다.
- 브라우저는 1000m/s마다 tick() 메서드를 호출한다 호출 → UI 업데이트 → 엇? <Clock/>변경됐네? → 2에서 4까지 반복하며 업데이트
- 만약 Clock 컴포넌트가 DOM으로부터 한번이라도 삭제되면, React는 componentWillUnmount() 생명주기를 호출해서 타이머를 멈춘다.
데이터는 아래로
컴포넌트는 자신의 state를 자식 컴포넌트에 props로 전달할 수 있다.
<FormattedDate date={this.state.date} />
FormattedDate 컴포넌트는 date를 props로 받게 됩니다.그리고 이게 어디서, 어떻게 왔는지 알 수 없다.
이 때문에 state의 특징으로 캡슐화를 이야기할 수 있다.
또한 부모에서 자식으로 데이터가 흐르기 때문에이를 "하향식(top-down)" 또는 "단방향식" 데이터 흐름이라고 한다.
그리고 이렇게 아래로 흐른 컴포넌트는 부모가 같다해도 서로는 완전히 독립적이다.
function App() { return ( <div> <Clock /> <Clock /> <Clock /> </div> ); } ReactDOM.render( <App />, document.getElementById('root') );
각 Clock은 자신만의 타이머를 설정하고 해지하게 됩니다.
출처
'JavaScript > React' 카테고리의 다른 글
create-react-app (0) | 2021.03.08 |
---|---|
리액트 시작하기-기본적으로 필요한 툴 (0) | 2021.03.08 |
11. 합성 vs 상속 (0) | 2021.02.05 |
6. 이벤트 처리 (0) | 2021.02.02 |
5.1 State 올바르게 사용하기 (0) | 2021.02.01 |
Uploaded by Notion2Tistory v1.1.0