react

[react] state와 생명주기

소영 2021. 11. 25. 17:08
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)