1. 리액트 라이프 사이클
* 라이프 사이클
- 클래스 컴포넌트에서는 랜더링이 되는 순간, 즉 컴포넌트가 돔에 붙는 순간 특정 기능을 실행할 수 있다. 그리고 리랜더링 될 때, 컴포넌트가 제거될 때에도 특정 기능을 부여할 수 있다.
- componentDidMount() : 컴포넌트가 첫 랜더링 되는 순간 실행할 함수를 선언할 수 있다. 주로 비동기 요청( ex. setInterval)을 많이 넣는다.
- componentWillUnmount() : 컴포넌트가 지워지기 직전에 실행되며 주로 componentDidMount에서 정의된 동작을 정리하기 위함이다. 세트로 묶어서 작성해야한다. 대부분 부모가 현 컴포넌트를 제거할 때 실행된다.
- componentDidUpdate() : 리랜더링 후에 실행되는 함수를 정의한다.
- 위를 리액트 클래스 컴포넌트의 라이프 사이클로 정리하면 다음과 같다.
constructor와 메서드들이 클래스에 붙는다 -> render 감지 -> ref 감지 -> componentDidMount 실행 ->
( setState/props가 바뀜 -> shouldComponentUpdate(true) -> render -> componentDidUpdate ) -> 부모가 현 컴포넌트를 제거 -> componentWillUnmount실행
- DidUpdate의 경우 초기화를 하고 재실행을 하는 버튼을 만들었다고 치자. 버튼은 초기화만 되도록 하고, DidUpdate에 필요한 함수를 넣으면 좋다. 하지만 렌더가 될 때마다 실행되므로 조건문을 작성해야 적절하게 사용된다. 초기화 되었을 때의 상태를 이용해서 조건을 작성하면 적절하다.
- 참고로 hooks에는 위와 같은 라이프 사이클은 없다. 하지만 다른 방식으로 비슷하게 구현은 가능하므로 아래에서 따로 살펴보자.
2. setInterval 사용하기
* class
- 클래스 컴포넌트에서는 위에서 봤던 기능을 이용해서 setInterval을 구현할 수 있다.
- 이미지 세 개가 번갈아 가면서 보이고, 버튼을 누르면 일시정지가 되었다가 다시 화면이 바뀌는 컴포넌트를 구현한다고 하자.
- class 컴포넌트를 선언하고 state를 선언한다. 그리고 별도로 interval이라는 변수를 두고 this.interval로 사용한다.
interval;
componentDidMount() {
this.interval = setInterval(this.changeHand, 100);
}
componentWillUnmount() {
clearInterval(this.interval);
};
- 처음 랜더링 되었을 때 실행해야 하므로 componentDidMount에 넣어준다. 이미지가 번갈아가는 함수는 별도로 changHand로 선언하였고, 0.1 초마다 함수가 실행될 것이다.
- 라이프사이클에서도 봤듯이 componentDidMount와 componentWillUnmount는 세트이다. 반드시 clear을 해주는 곳이 있어야한다.
- 논외이지만 changeHand함수를 잠시 살펴보자.
changeHand = () => {
const {imgCoord} = this.state;
if (imgCoord === rspCoords.one) {
this.setState({
imgCoord: rspCoords.two,
});
} else if (imgCoord === rspCoords.two) {
this.setState({
imgCoord: rspCoords.three,
});
} else if (imgCoord === rspCoords.three) {
this.setState({
imgCoord: rspCoords.one,
});
}
};
- imgcoord를 조건으로 검사하는데 이를 this.state에서 받아오는 것을 알 수 있다. 이 때 const 문을 changeHand바깥에서 선언하면 클로저 문제가 발생한다. 비동기 안에서는 바깥의 내용을 참조하면 안된다. 처음 state가 one이었다면 항상 one을 받아서 two로 바꾸기만 할 것이다. 그러니 반드시 setInterval함수 내에서 받을 수 있도록 해야한다. 참고로 비동기에서 let을 쓰면 클로저 문제를 피할 수 있다. 즉 for문과 같은 곳에서 인덱스로 let i를 선언하는 것은 괜찮다는 것이다. (ES6)
onClickBtn = (choice) => () => {
const {imgCoord} = this.state;
clearInterval(this.interval);
//...
//...
setTimeout(() => {
this.interval = setInterval(this.changeHand, 100);
}, 1000);
};
- 일시 멈춤 기능을 위한 버튼을 만들면 onClick에 넣어줄 함수를 만들어야한다. 여기서는 clearInterval을 해주고 필요한 기능들을 담아주면 된다. 그리고 일시정지될 시간을 담아서 setTimeout안에 다시 실행될 setInterval과 넣으면 완성이다.
- 참고로 위의 함수는 고차함수로 되어있다. 이를 알아보기 위해서 render부분을 살펴보자.
<button id="one" className="btn" onClick={this.onClickBtn('one')}>1</button>
<button id="two" className="btn" onClick={this.onClickBtn('two')}>2</button>
<button id="three" className="btn" onClick={this.onClickBtn('three')}>3</button>
- 태그에서 onclick부분에는 반드시 함수가 들어가야한다. 따라서 위의 코드를 고차함수로 쓰지 않으려면 내부에 ( )=> this.onClickBtn('one') 과 같은 식으로 써야하지만 고차함수로 만듦으로써 간단하게 this. 으로 시작할 수 있게 된 것이다.
- 자세히 알아보자면 이전 장 까지는 this.onClick~~ 자체가 함수였었다. 그러나 이번에는 this.onClickBtn('one') 은 함수가 아닌 그 함수의 결과물이 되버린다.
* Hooks
- hooks는 라이프사이클이 없어서 useEffect를 사용해야한다.
- useEffect를 사용하려면 useRef처럼 먼저 불러와야한다.
const { useState, useRef, useEffect } = React;
- 그리고 반드시 함수컴포넌트 안에서 사용해야한다.
const [imgCoord, setImgCoord] = useState(rspCoords.one);
const interval = useRef();
useEffect( () => {
interval.current = setInterval(changeHand,100);
return () => {
clearInterval(interval.current);
}
}, [imgCoord] )
- 컴포넌트 안을 보면 state를 정의하고, 임의의 interval이라는 변수에 useRef()를 할당하였다.
- useEffect에서 함수를 정의하는데 이 때 componentDidMount 또는 componentDidUpdate의 역할을 하게되는 블럭을 작성한다. 그리고 return에 함수로써 componentWillUnmount부분을 작성한다.
- 두 번째 인자로 배열을 넣고, 변경을 원하는 state를 작성해준다. 참고로 넣지 않으면 처음에 한번만 실행되어서 Interval의 역할을 제대로 하지 못한다.
- 위가 동작하는 과정을 생각해보자. 함수 컴포넌트는 랜더링마다 통째로 다시실행된다. useEffect도 계속 다시 실행되게 된다. setInterval이 실행되었다가 꺼졋다가를 반복하면서 기존의 setInterval의 효과를 보여준다.
- useEffect는 여러개를 선언하여 여러 state를 각각 감지할 수도 있고, 배열에 한 개이상을 넣어서 여러 state를 감지할 수도 있다.
- 클래스에서와의 차이점을 굳이 정리하자면, 클래스에서는 어떠한 변경과 함께 순차적으로 사이클이 실행되며 메소드드들이 동반한다. 하지만 hooks에서는 배열의 state가 변경될 때 마다 실행되는 사이클을 만든다.
- 참고로 setInterval을 꼭 useEffect나 componentDidMount에서 쓸 필요는 없다. 그러나 절대 랜더링에서 쓰면 안되며, 꼭 WillUnmount와 같은 곳에서 정리는 필수이다.
참고
이 글은 ZeroCho 님의 리액트 무료 강좌를 수강하며 개인적으로 정리하며 쓰는 글입니다.
인프런
유튜브
'Client > React.js' 카테고리의 다른 글
<리액트 기초> useMemo & useCallback (0) | 2021.02.24 |
---|---|
<리액트 기초> useEffect (0) | 2021.02.22 |
<리액트 기초> setTimeout 사용하기 (0) | 2021.02.17 |
<리액트 기초> 조건부 연산자 (0) | 2021.02.15 |
<리액트 기초> Ref와 포커싱 (0) | 2021.02.12 |