함수형 컴포넌트에서도 state관리, lifecycle과 같은 class형 컴포넌트에서만 가능하던 기능들을 사용할 수 있도록한 것이다.
부분적인 API 사용 및 state를 관리하는 로직을 재사용하는데 제약이 있다.
constructor, this, binding 등 기본 규칙을 따라야하기 때문에 코드가 복잡해진다. 또, lifecycle때문에 같은 로직을 다른 method내에서 사용하는 경우가 있다.(componentDidMount, componentWillUnmount)
같은 로직을 한 곳으로 모을 수 있기 때문에 응집성이 좋아지고 가독성이 좋아진다.
함수안에서 다른 함수를 호출해 새로운 훅을 만들 수 있다. 클래스형 컴포넌트의 lifecycle method를 useState와 useEffect를 조합해 표현할 수 있다.
import React, { useState } from 'react';
function Example() {
const [count, setCount] = useState(0); // count의 초기값 '0'
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
useState
를 이용해 count
라는 state변수와 state값을 변화시켜주는 함수 setCount
를 만든다. useState의 인자는 state변수의 초기값이 된다.
버튼을 클릭하면 setCount
를 호출해서 state값을 변화시킨다.
-클래스형 컴포넌트의 componentDidMount
와 componentDidUpdate
, componentWillUnmount
가 합쳐진 것이다.
-렌더 이후 어떤 일을 수행해야하는지를 말한다.
-렌더 이후 매번 수행된다.
-componentWillUnmount
가 필요없는 경우
useEffect(() => {
document.title = `You clicked ${count} times`;
});
렌더할때마다 state변수 count를 불러온다. 함수안에서 useEffect를 불러내기 때문에 state변수에 접근할 수 있다.(자바스크립트의 클로저를 이용)
-componentWillUnmount
가 필요한 경우
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
}; // 정리를 위한 함수
});
정리를 위한 함수를 반환해 구독을 추가, 제거하는 로직을 묶어둔다. 하나의 effect를 구성한다. 렌더될 때마다 useEffect가 실행되므로 다음 effect가 실행하기 전에 이전의 렌더링에서 파생된 effect를 정리하게 된다. 이것은 클래스형 컴포넌트의 componentDidUpdate
와 componentWillUnmount
에서의 중복을 피하고 관련 코드를 한곳에 모아두기 때문에 버그를 방지하는데 도움이 된다.
componentDidUpdate
를 제대로 다루지 않는다는 것이다.
위의 코드를 클래스형으로 작성할 경우, componentDidUpdate
가 필요하다.
componentDidMount() {
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentDidUpdate(prevProps) {
// 이전 friend.id에서 구독을 해지합니다.
ChatAPI.unsubscribeFromFriendStatus(
prevProps.friend.id,
this.handleStatusChange
);
// 다음 friend.id를 구독합니다.
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
컴포넌트가 화면에 표시되어 있는 동안 friend.id
가 변화하면 마운트해제가 일어날때는 다른 친구의 id를 사용하게 된다. 이를 해결하기 위해서 componentDidUpdate
를 사용하는 것이다. friend.id
가 변화하게 되면 componentDidUpdate
가 실행되어 이전의 friend.id
는 구독해지 하고 변화한 friend.id
를 구독한다.
훅에서는 effect가 렌더할 때마다 실행되기 때문에 렌더할때마다 이전의 effect를 정리해 componentDidUpdate를 사용하지 않아 생기는 문제를 쉽게 해결할 수 있다.
하지만 모든 렌더링마다 effect를 실행시키는 것은 성능저하 문제를 야기할 수 있다. 이를 해결하기 위해
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
}, [props.friend.id]); // props.friend.id가 바뀔 때만 재구독합니다.
useEffect의 두번째 인자로 배열을 넘기면 이것이 변화할때마다 실행해 최적화가 가능하다.
이것은 componentDidMount
, componentWillUnmount
,componentDidUpdate
를 useEffect
하나로 대체할 수 있는 것을 의미한다.
useEffect의 두번째 인자로 []
빈배열을 넘기면 effect가 props, state를 모두 의존하지 않게되므로 재실행되지 않고 렌더 후 딱 한번만 실행된다.
이것은 componentDidMount
와 componentWillUnmount
에 가깝다.