this
키워드는 혼란을 야기한다.Hook은 React 16.8에 새로 추가된 기능입니다. Hook은 class를 작성하지 않고도 state와 다른 React의 기능들을 사용할 수 있게 해줍니다.
Hook
을 사용함으로써 function
에서도 class
에서 사용하던 상태관리와 생명주기를 제어할 수 있게 되었다. 또한 코드를 간결하고 직관적으로 작성할 수 있는 장점까지 갖췄다니 안써볼 수 없겠다.
여러가지 Hook이 있으나 가장 기본적인 useState
, useEffect
에 대해서 알아보도록 한다.
리액트에서 가장 흔하게 사용되는 counter 예제를 통해 살펴보자 먼저 클래스를 사용해서 작성한다면 다음과 같이 될 것이다.
class Counter extends React.Component{
constructor(props) {
super(props);
this.state = {
count: 0
};
this.handleCountPlus = this.handleCountPlus.bind(this);
this.handleCountMinus = this.handleCountMinus.bind(this);
}
handleCountPlus(){
this.setState({
count:this.state.count + 1
});
}
handleCountMinus(){
this.setState({
count:this.state.count + 1
});
}
render(){
return(
<div>
<h2>Class Component</h2>
<p>Count number : {this.state.count}</p>
<button onClick={this.handleCountPlus}>Plus</button>
<button onClick={this.handleCountMinus}>Minus</button>
</div>
);
}
}
위 코드에서 state
는 {count:0}
이고 각 버튼 클릭이벤트에서 this.setState()
를 사용하여 count 상태를 변경한다. 그리고 이제 useState
를 사용한 아래 코드를 보자.
import React, {useState} from 'react';
function Counter(){
const [count, setCount] = useState(0);
const handleCountPlus = () =>{
setCount(count+1);
}
const handleCountMinus = () =>{
setCount(count-1);
}
return (
<div>
<h2>Function Component</h2>
<p>Count number : {count}</p>
<button onClick={handleCountPlus}>Plus</button>
<button onClick={handleCountMinus}>Minus</button>
</div>
);
}
위 코드는 클래스로 만든 코드와 동일하게 동작한다. 클래스 컴포넌트에 비해 코드의 양이 많이 줄고 this
또한 사용하지 않으니 코드가 매우 직관적으로 보인다. 이제 어떻게 useState
를 사용하는지 살펴보자.
import React, {useState} from 'react';
먼저 useState
를 사용해주기 위해 리액트 패키지에서 제공하는 useState
를 불러온다.
const [count, setCount] = useState(0);
그 다음 useState(0)
에 넣은 인자값 0을 초기값으로 지정하고 반환된 첫번째 값은 상태(count) 두번째 값은 상태를 변경할시 호출하는 함수(setCount, setter 함수라고 부른다)이다.
const countState = useState(0);
let count = countState[0];
let setCount = countState[1];
위 코드와 같이 사용할 수도 있지만 구조분해 할당을 통해 간단하게 사용할 수 있다.
const handleCountPlus = () =>{
setCount(count+1);
}
const handleCountMinus = () =>{
setCount(count-1);
}
return (
<div>
<h2>Function Component</h2>
<p>Count number : {count}</p>
<button onClick={handleCountPlus}>Plus</button>
<button onClick={handleCountMinus}>Minus</button>
</div>
);
그리고 각 버튼 클릭이벤트에 setCount
를 사용하여 인자로 받은 값을 최신상태로 변경해 준다.
위에서 작성한 코드 예제의 실행 결과는 아래에서 확인할 수 있다.
이제 useEffect
를 사용하여 컴포넌트가 마운트 됐을 때, 언마운트 됐을 때 그리고 업데이트 되었을 때에 특정한 작업을 하는 방법을 알아보자. 이번에는 시계 컴포넌트를 예로들어 코드를 작성한다.
import React, { useState, useEffect } from "react";
function Clock(){
const [datetime, setDatetime] = useState(new Date());
const [tickCount, setTickCount] = useState(0);
useEffect(()=>{
console.log("화면에 나타남(Did mount)");
const intervalId = setInterval(() => {
setDatetime(new Date());
}, 1000);
return () => {
console.log("화면에서 사라짐(Will Unmount)");
clearInterval(intervalId);
};
},[]);
useEffect(()=>{
console.log("상태가 갱신됨(Did update)");
setTickCount(tickCount + 1);
}, [datetime]);
return (
<div>
<h2>Now Datetime is : {datetime.toLocaleTimeString()}</h2>
<h2>Tick Count : {tickCount}</h2>
</div>
);
}
위 예제 코드는 컴포넌트가 마운트됐을 때 setInterval
을 사용하여 매 초마다 datetime
상태를 갱신 시키다가 언마운트 될 때 clearInterval
을 사용하여 매초마다 실행시키던 함수를 종료한다. 이제 useEffect
사용방법에 대해 살펴보자.
import React, { useState, useEffect } from "react";
useEffect
를 사용하기 위해 리액트 패키지에서 제공하는 useEffect
를 불러온다.
useEffect(()=>{
console.log("화면에 나타남(Did mount)");
// Do something...
return () => {
console.log("화면에서 사라짐(Will unmount)");
// cleanup function
// Do something...
};
},[]);
useEffect
의 첫번째 인자는 실행시킬 함수, 두번째 인자는 의존값이 들어있는 배열을 줄 수 있다. 만약 두번째 인자값을 빈 배열로 사용한다면, 컴포넌트가 처음나타날 때(Did mount) 첫번째 인자로 넘겨준 함수가 호출되고, return
한 함수에서는 컴포넌트가 사라질 때(Will unmount) 실행된다. 이를 cleanup
함수라고 부른다.
주로 마운트 될 때 하는 작업은 다음과 같은 사항들이 있다.
setInterval
을 통한 반복 작업 혹은 setTimeout
을 통한 작업 예약그리고 언마운트 할 때 하는 작업은 다음과 같다.
setInterval
, setTimeout
을 사용하여 등록한 작업들 clear 하기 (clearInterval
, clearTimeout
)useEffect(()=>{
console.log("상태가 갱신됨(Did update)");
// 특정 상태가 변했을때 호출할 함수 내용 작성
return ()=>{
console.log("상태가 바뀌기 전 호출 Will update)");
};
},[state]);
그리고 위와 같이 useEffect
의 두번째 인자에 특정값을 넣었을 때에는 컴포넌트가 마운트 될 때와 지정한 값이 바뀔 때 호출이 된다. 그리고 return
에 함수를 작성했다면 컴포넌트가 언마운트 될 때와 지정한 값이 바뀌기전의 상태에서 함수를 실행할 수 있다. 두번째 인자를 생략하면 컴포넌트가 리렌더링(render)될 때마다 호출된다.
아래는 예제코드를 약간 변경하여 체크박스의 체크여부에 따라서 useEffect
가 어떻게 동작하는지 알기 편하도록 했다.
Hook
사용할 때 두가지 규칙을 준수해야 한다. 이러한 규칙을 강제하기 위해서 제공하는 linter 플러그인도 제공한다고 하니 관심이 있다면 알아보도록 하자.
반복문이나 조건문 또는 중첩된 함수에서 Hook
을 호출하면 안된다. 위 규칙을 따르지 않으면 컴포넌트가 제대로 작동하지 않을 수 있다.
또 한가지 유념할 점은 Hook
은 클래스 안에서는 동작하지 않는다는 점이다. 바꿔 말하면 함수형 컴포넌트에서만 Hook
을 사용할 수 있다.
Hook
을 사용함으로써 클래스 컴포넌트를 작성할 때 보다 코드를 간결하고 직관성있게 작성할 수 있다.useState
, useEffect
로 함수 컴포넌트에서도 상태 및 생명주기 관리를 할 수 있다.Hook
에서는 지켜야할 두 가지 규칙이 있고, 이를 지켜야 정확한 동작을 보장한다.