Hook은 함수 컴포넌트에서 react state와 lifecylcle을 연동 할 수 있게 해주는 함수이다.
hook은 class 안에서는 동작하지 않는다. 대신 class 없이 react를 사용할 수 있게 해준다.
최상위에서만 Hook을 호출해야 한다. 그리고 반복문, 조건문, 중첩된 함수 내에서는 Hook을 실행해서는 안된다.
react 함수 컴포넌트 내에선만 hook을 호출해야 한다. 일반 javascript 함수에서는 hook을 호출해서는 안된다.
const [state, setState] = useState(initailState);
상태 유지 값과 그 값을 갱신하는 함수를 반환한다. 최초로 렌더링을 하는 동안, 반환된 state(state)는 첫번째 전달된 인자(intialState)의 값과 같다.
setState함수는 state를 갱신할 때 사용한다. 새 state 값을 받아 컴포넌트 리렌더링을 큐에 등록한다.
example code
// 클래스형 컴포넌트
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}
import React, { useState } from 'react';
// 함수 컴포넌트
function Example() {
// 새로운 state 변수를 선언하고, count라 부르겠습니다.
const [count, setCount] = useState(0);
const [isModalActive, setIsModalActive] = useState(false);
// 안 좋은 예시
const [state, setState] = useState({
color: "red",
isActive: true,
name: "jt".
password: "sdaf"
})
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
<button onClick={() => setIsModalActive(!isModalActive)}>modal btn</button>
</div>
);
}
- 기존 setState와 마찬가지로 비동기 update
- useState 실행 시 state, setState 한쌍이 부여됨
함수는 상태를 저장하지 않는다
함수는 실행이 완료되면 함수 내에서 사용했던 모든 메모리들을 정리한다. 이를 가비지 컬렉팅
이라고 한다. 2018년 이전까지 React에서 상태관리 및 라이프사이클 API를 사용하기 위해 클래스형 컴포넌트를 사용했던 이유다.
하지만 함수가 언제나 가비지 컬렉터에 의해 사라지기만 하는 건 아니다. 실행이 끝나고도 메모리에 스스로를 남겨둘 수 있는 방법이 있다. 바로 클로저
다.
하지만 클로저가 출동한다면 어떨까?
함수형 컴포넌트는 Hooks를 사용하면서 상태 관리가 가능해졌다. Hooks에서 state를 저장하려면 useState()
를 사용한다. useState 역시 함수이며 클로저를 이용해 변수를 저장한다. 간단한 코드로 확인해보자.
const useState = (init = undefined) => {
let value = init
const getter = () => value // 클로저
const setter = next => (value = next) // 클로저
return [getter, setter]
}
const [state, setState] = useState('클로저')
위 코드는 간략하게나마 useState의 내부 구조를 보여주고 있다.
초기값을 받아 내부의 지역변수 value
에 할당한다. 내부 함수 getter()
는 지역변수 init을 바라보고 있다. 또 다른 내부 함수 setter()
는 next라는 인자를 받아 value의 값을 수정한다. 이후 다시 getter()
를 호출하게 되면 변경된 value의 값을 호출하게 된다.
두 함수는 배열 형태로 리턴되고 useState
를 사용할 때는 배열 구조분해 할당 형태로 많이 사용하게 된다.
일반적인 함수라면 return과 함께 실행이 종료되고 나서 내부의 데이터들이 가비지 컬렉팅 되어야 하겠지만, 이 경우 내부 함수가 지역 변수를 참조하고 있으므로 사라지지 않는다.
또한 외부로 노출된 getter, setter 함수를 통해 내부 변수에 지속적으로 접근하며 호출/재할당을 할 수 있다. 이는 클래스형 컴포넌트에서 state가 해온 역할과 동일하다.
Effect Hook, 즉 useEffect는 함수 컴포넌트 내에서 이런 side effects를 수행할 수 있게 해준다. React class의 componentDidMount 나 componentDidUpdate, componentWillUnmount와 같은 목적으로 제공되지만, 하나의 API로 통합된 것이다.
useEffect(function);
useEffect(() => {}, [count]) // 의존성 배열 (배열 안에 담긴 값들을 추적, 그때 마다 업데이트)
useEffect(() => {
console.log("componentDidMount")
return () => console.log("componentWillUnmount")
}, [])
useEffect(() => {
console.log("componentDidMount")
console.log("componentDidUpdate") // + shouldComponentUpdate
return () => console.log("???")
}, [state])
useEffect(() => {
console.log("componentDidMount")
return () => console.log("componentWillUnmount")
}, [])
cosnt [isMounted,setIsMounted] = useState(false);
useEffect(() =>{
if(!isMounted) {
console.log("CDM")
setIsMounted(true)
return
}
console.log("CDU")
})