| 클래스형 컴포넌트 | 함수형 컴포넌트 | |
|---|---|---|
| state 사용 가능 여부 | 가능 | 불가능했지만, 리액트 버전 16에 도입된 hook을 통해 사용 가능해짐 |
| 라이프 사이클 메서드 사용 가능 여부 | 가능 | 불가능했지만, 리액트 버전 16에 도입된 hook을 통해 라이프 사이클마다 원하는 동작을 할 수 있게 됨 |
| 메모리 | 함수형 컴포넌트보다 메모리를 조금 더 차지함 | 클래스형 컴포넌트보다 메모리를 조금 덜 차지함 |
| 재사용 가능한 로직 구현 | render props, 고차 컴포넌트 패턴으로 구현하고, 라이프 사이클 메서드 기준으로 컴포넌트를 나눠야 하기 때문에 버그가 쉽게 발생하고 무결성 확보가 어려움 | Hook을 활용하여 로직을 추상화할 수 있기 때문에 로직 기준으로 컴포넌트를 잘게 쪼갤 수 있어 코드 추적과 무결성 확보가 용이함 |
| 코드의 간결성 | ↓ | ↑ |
| 사용 권장 | 권장하지 않음 | 공식 문서에서는 함수형 컴포넌트와 훅을 함께 사용할 것을 권장 |
클래스형 컴포넌트
// class 키워드로 시작 // Component로 상속받음
class MyComponent extends Component {
// render() 메서드로 jsx 반환
render() {
return (
...
);
}
}
함수형 컴포넌트
// 함수로 작성
const MyComponent = () => {
// return 문으로 jsx 반환
return (
...
);
}
class형 컴포넌트
class형 컴포넌트에서 state는 항상 객체 형태
//constructor 내부에서 초깃값 설정
constructor(props) {
super(props)
this.state = {
....
}
}
// 또는 직접 초깃값 설정
class myComponent extends Component {
state = {
....
}
}
// this로 state 조회
this.state.myState;
// this.setState로 state 값 변경
this.setState({변경할 값})
함수형 컴포넌트
useState hook 사용
// 초깃값 설정 및 state 조회
const [state, setState] = useState(초기값);
// state 값 변경
setState(변경할 값)
클래스형 컴포넌트
// this 키워드로 props 조회
const { props1, props2 } = this.props;
// defaultProps 설정
MyComponent.defaultProps = {
...
}
// 또는 static 키워드와 함께 선언
static defaultProps = {
...
}
함수형 컴포넌트
const MyComponent = ({props1, props2}) => {
...
}
클래스형 컴포넌트
class myComponent extends Component {
// 생성자 메서드에서 핸들러를 bind 하거나,
constructor(props) {
super(props);
this.handleClick= this.handleClick.bind(this);
}
handleChange() {
....
}
// bind 메서드를 사용하지 않고 화살표 함수 사용
handleChange = () => {
....
}
render() {
// this로 핸들러 할당
<input onChange={this.handleChange}/>
}
}
함수형 컴포넌트
const myComponent = () => {
// const 키워드로 선언
const handleChange = () => {
...
}
return (
// 핸들러 할당
<input onChange={handleChange}/>
)
}
1. 마운트
// 1. constructor
// 컴포넌트 생성자 메서드
// 가장 먼저 실행(render 전에 호출)
constructor(props) {
super(props)
}
// props로 받아온 것을 state에 넣어주고 싶을 때 사용
// render 메서드 호출 직전에 호출됨
static getDerivedStateFromProps(nextProps, prevState) {
...
}
// 컴포넌트를 렌더링하는 메서드
// 사이드 이펙트를 발생시키면 안 됨
render() {
...
}
// render 메서드의 첫 번째 반환값이 실제 돔에 반영된 직후 호출
// 외부 api호출, DOM 에 관련된 작업
componentDidMount() {
...
}
2. 업데이트
// 컴포넌트의 리렌더링 여부를 결정
// 컴포넌트 최적화 시 활용
// props나 state가 바뀌었을 때 호출됨
shouldComponentUpdate(nextProps, nextState) {
return true;
// 기본값은 true
// false를 반환하면 업데이트 X
}
// 렌더링 결과가 실제 돔에 반영되기 직전에 호출
getSnapshotBeforeUpdate(prevProps, prevState) {
...
}
// 업데이트 단계에서 마지막으로 호출됨
// 가상 돔이 실제 돔에 반영된 후 호출됨
// 새로 반영된 돔의 상태값을 가장 빠르게 가져올 수 있는 메서드
componentDidUpdate(prevProps, prevState, snapshot) {
// 3번째 파라미터로 getSnapshotBeforeUpdate에서 반환한 값을 조회
}
3. 언마운트
// 컴포넌트가 화면에서 사라지기 직전에 호출
// 주로 사전에 해당 컴포넌트에 등록했었던 이벤트 및 연관 외부 라이브러리 호출 등을 제거
componentWillUnmount() {
...
}
useEffect(() => {
// 렌더링 될 때마다 실행:
// 1. 마운트(렌더 직후)
// 2. 업데이트
// - 의존성 배열이 없다면 업데이트 될 때마다
// - 의존성 배열이 비어있다면 업데이트 시 실행X
// - 의존성 배열의 값이 있다면 해당 값이 변경될 때마다
return () => {
// 컴포넌트의 언마운트 직전에 실행
};
}, []);
// 리렌더링 여부를 결정
// 컴포넌트 최적화 시 활용
const cachedFn = useCallback(() => {
...
}, [])
const cachedValue = useMemo(() => {
...
},[])
// 렌더링 결과가 실제 돔에 반영되기 직전에 호출
useLayoutEffect(() => {
...
},[])
// 로직이 복잡할 경우 사용자가 변경된 레이아웃을 보는데까지 시간이 오래 걸린다는 단점이 있기 때문에,
// 기본적으로는 항상 useEffect만을 사용하는 것을 권장함.
// 하지만 첫 렌더링 시 보여지는 레이아웃과 보여줘야 하는 레이아웃이 달라 화면에 깜빡이는 발생하는 상황에서는 useLayoutEffect를 사용하는 것이 좋음.