* 함수 컴포넌트와 클래스 컴포넌트의 차이
* 함수 컴포넌트에서 Hook을 사용하는 이유
* Hook의 개념 및 사용 규칙
React Hooks는 React 16.8 버전부터 추가된 기능으로, React에는 다양한 Hook이 존재하고 있다.
- Hook이란, 클래스 컴포넌트와 생명주기 메서드를 이용하여 작업을 하던 기존 방식에서 벗어나 함수형 컴포넌트에서 더 직관적인 함수를 이용하여 작업할 수 있게 만든 기능이다.
- 뛰어난 재사용성과 직관성을 갖는 함수형 컴포넌트와 훅은 사용률이 매우 높은 편이다.
Class Component & Function Component
Hook은 함수 컴포넌트에서 사용하는 메서드이다. 함수 컴포넌트 이전에는 클래스(class) 컴포넌트가 있었다.
Class Component
아래는 <Counter /> 컴포넌트를 클래스로 작성한 코드이다.
Class 컴포넌트로 작성한 Counter 컴포넌트
class Counter extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0
}
this.handleIncrease = this.handleIncrease.bind(this);
}
handleIncrease = () => {
this.setState({
counter: this.state.counter + 1
})
}
render(){
return (
<div>
<p>You clicked {this.state.counter} times</p>
<button onClick={this.handleIncrease}>
Click me
</button>
</div>
)
}
}
단순히 카운팅을 하는 기능만 구현한 것이다.
- 함수 컴포넌트 이전의 클래스 컴포넌트로 작성할 때에는 이 정도는 작성을 해야지 간단한 앱이 정상적으로 동작할 수 있었다.
- 이런 클래스 컴포넌트는 복잡해질수록 이해하기 어려워졌고, 컴포넌트 사이에서 상태 로직을 재사용하기 어렵다는 단점이 있었다.
- 또한 React의 클래스 컴포넌트를 사용하기 위해서는 JavaScript의 this 키워드가 어떤 방식으로 동작하는지 알아야 하는데, 이는 문법을 정확히 알지 못하면 동작 방식 자체를 정확히 이해하기 어렵게 만들곤 했다.
그래서 React는 점진적으로 클래스 컴포넌트에서 함수 컴포넌트로 넘어갔다. 다만 이전까지의 함수 컴포넌트는 클래스 컴포넌트와는 다르게 상태 값을 사용하거나 최적화할 수 있는 기능들이 조금 미진했는데, 그 부분들을 보완하기 위해 Hook이라는 개념을 도입했다.
Class(클래스) 컴포넌트는 componentDidMount(), componentDidUpdate() 등 렌더링 시점에 따라 어떻게 동작시킬 지 설정할 수 있는 메소드들이 존재하고 있었다.
Function Component
아래는 <Counter /> 컴포넌트를 함수형 컴포넌트로 작성한 코드이다.
Function으로 작성한 Counter 컴포넌트
function Counter () {
const [counter, setCounter] = useState(0);
const handleIncrease = () => {
setCounter(counter + 1)
}
return (
<div>
<p>You clicked {counter} times</p>
<button onClick={handleIncrease}>
Click me
</button>
</div>
)
}
함수형 컴포넌트는 클래스형 컴포넌트에 비해 훨씬 더 직관적이고, 보기 쉽다는 특징이 있다.
- 이 Counter 컴포넌트에서 숫자를 올리기 위해 상태값을 저장하고 사용할 수 있게 해주는 useState()가 있는데, 이 메서드가 바로 Hook이다.
- 다시 말하자면, Counter 컴포넌트에서 useState() Hook을 호출해 함수 컴포넌트 안에 state를 추가한 형태이다.
- 이 state는 컴포넌트가 리렌더링 되어도 그대로 유지가 된다.
- 또한 State Hook은 여러 개 사용할 수 있다.
Hook이란?
React의 공식문서를 보면 Hook에 대해 이런 문구가 있다.
Hook은 React 16.8에 새로 추가된 기능입니다.
Hook은 class를 작성하지 않고도 state와 다른 React의 기능들을 사용할 수 있게 해 줍니다.
Hook은 다르게 말하면 함수형 컴포넌트에서 상태 값 및 다른 여러 기능을 사용하기 편리하게 해주는 메서드를 의미한다.
Hook은 class가 아닌 function으로만 React를 사용할 수 있게 해주는 것이기 때문에 클래스형 컴포넌트에서는 동작하지 않는다.
...
render(){
/* 클래스 컴포넌트는 render() 안에서 변수를 작성할 수 있다. */
const [counter, setCounter] = useState(0);
...
}
클래스 컴포넌트에서는 useState() Hook을 호출할 수 없다는 에러가 뜬다.
억지로 호출을 해보려고 해도 해당 방식은 React에서 허락하지 않는 호출 방식이기 때문에 위와 같은 에러를 브라우저 전면에 띄운다.
해당 에러를 삭제하면 컴포넌트 자체가 렌더링이 되지 않는 것을 볼 수 있다.
Hook 사용 규칙
Hook을 사용할 때는 두 가지 규칙을 준수해야만 한다.
1. 리액트 함수의 최상위에서만 호출해야 한다.
반복문, 조건문, 중첩된 함수 내에서 Hook을 실행하면 예상한 대로 동작하지 않을 우려가 있다.
예를 들어 count가 있을 때 sample이라는 state를 사용하고 싶어서 조건문 안에 useState() hook을 불러왔다고 가정해 보자.
- (이런 식의 가정은 애초부터 틀렸으며, 예시를 위한 가정이다.)
...
if(counter) {
const [sample, setSample] = useState(0);
}
...
이런 식으로 호출을 하게 되면 React의 동작 방식에 거스르기 때문에 React는 에러를 출력한다.
useState를 조건문 안에서 호출하면 안 된다는 Error
이유는 다음과 같다.
- 컴포넌트 안에는 useState나 useEffect 같은 Hook들이 여러 번 사용될 수 있다.
- React는 이 Hook을 호출되는 순서대로 저장을 해놓는다.
- 그런데 조건문, 반복문 안에서 Hook을 호출하게 되면 호출되는 순서대로 저장을 하기 어려워진다.
- 결국 예기치 못한 버그를 초래하게 될 수 있다.
2. 오직 리액트 함수 내에서만 사용되어야 한다.
이는 리액트 함수형 컴포넌트나 커스텀 Hook이 아닌 다른 일반 JavaScript 함수 안에서 호출해서는 안 된다는 의미이다.
예를 들어 window의 요소가 모두 준비가 되면 useEffect()가 호출되었으면 좋겠다고 생각해서 함수를 작성했다고 가정해 보자.
- (이런 식의 가정은 애초부터 틀렸으며, 예시를 위한 가정이다.)
...
window.onload = function () {
useEffect(() => {
// do something...
}, [counter]);
}
...
이 또한 React의 동작 방식에 위배되기 때문에 React는 에러를 출력한다.
window.onload라는 함수에서 useEffect를 호출하면 안 된다는 error
애초에 Hook은 React의 함수 컴포넌트 내에서 사용되도록 만들어진 메서드이기 때문에 근본적으로 일반 JavaScript 함수 내에서는 정상적으로 돌아가지 않는다. 따라서 이 규칙 또한 반드시 준수해야 하는 규칙이다.
'FE > React' 카테고리의 다른 글
React의 렌더링 최적화를 도와주는 useCallback (0) | 2023.05.19 |
---|---|
React의 렌더링 최적화를 도와주는 useMemo (0) | 2023.05.19 |
React Diffing Algorithm - 상태변화를 감지할 수 있도록 하는 React의 비교 알고리즘 (0) | 2023.05.19 |
[React] Virtual DOM (0) | 2023.05.19 |
[JS] useEffect 용례 (0) | 2023.05.04 |