React hook이 나오기 전 까지는 리액트 life cycle을 사용하기 위해서 Class를 사용해야 됐었습니다. 리액트를 클래스로 사용하기 위해서는 React.Component를 extend 해야 하고 state을 사용하려면 super(props)도 적어 줘야 합니다. 대부분은 이게 왜 필요한지 모르고 적고 있었습니다 하지만 없으면 안 돌아가니까 계속 적어주는 것이죠. 이런 부분은 자동으로 되면 좋을 것 같은데? 그리고 Class를 사용해서 재사용 가능한 로직을 만들려면 HOC(High-Order-Component)를 사용해서 만들어야 되는데 이걸 사용하다 보면 나중에는 HOC 헬을 격게 되고 코드도 이해하기가 상당히 힘들어집니다. state를 정의하고 업데이트하는데도 많은 코드가 필요했습니다. 이런 것을 해결하려고 나온 것이 React-hook입니다. 나중에 비교해 놓은 것을 보시면 알겠지만 코드가 만이 간결해지고 보기도 편해집니다.
리액트 훅이란?
리액트 훅은 간단하게 설명해서 클래스 형식의 리액트 컴포넌트에서만 할 수 있었던 state관리나 라이프사이클등을 함수 형태 컴포넌트에서도 사용할 수 있게 만들어주는 기능입니다.
먼저 리액트 클래스 형태일 때 state를 업데이트 하는 법 보기
만약에 홈 컴포넌트는 컴포넌트가 마운트 됐을 때 그리고 pathName prop이 변경됐을 때 lookups 데이터를 api에서 받아와야 한다고 했을 때 리액트 클래스 컴포넌트를 사용해서 만들었을 때는 아래와 비슷한 것 같습니다.
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
lookups: null
};
// this를 사용하기 위해서 아래와 같이 바이드를 해줍니다.
this.fetchLookups = this.fetchLookups.bind(this);
}
componentDidMount() {
this.fetchLookups(this.props.pathName);
}
componentDidUpdate(prevProps) {
// pathName prop이 변경 되었을 때
if (prevProps.pathName !== this.props.pathName) {
this.fetchLookups(this.props.pathName);
}
}
fetchLookups(pathName) {
fetchApi({ url: `/lookup?url=${pathName}` }).then((lookups) => {
// lookups 데이터가 반환 되면 state업데이트
this.setState({
lookups
});
});
}
render() {
if (!this.state.lookups) return null;
return <pre>{JSON.stringify(this.state.lookups)}</pre>;
}
}
React hook을 사용했을 때 바뀐 모습
클래스를 사용한 것을 함수 형태로 바꾸고 훅을 사용했을 때는 아래와 같습니다. 훨씬 간결해지고 클래스에서는 반복적이고 불필요한 느낌에 코드가 보이지 않습니다.
const HomeWithHook = ({ pathName }) => {
const [lookups, updateLookups] = useState(null);
useEffect(() => {
if (pathName) {
fetchApi({ url: `/lookup?url=${pathName}` }).then((lookups) => {
updateLookups(lookups);
});
}
// dependency를 pathName으로 해주면 맨 처음에 한번 pathName이 변경 되었을 때 또 실행
}, [pathName]);
if (!lookups) return null;
return <pre>{JSON.stringify(lookups)}</pre>;
};
클래스 컴포넌트에 불만 사항들?
super(props) 항상 해줄 필요가 있나?
클래스 형태로 리액트 컴포넌트를 만들고 state를 사용한다면 super(props)를 해줘야 합니다. React.Component에도 props을 넘겨주기 위한 것이죠. 하지만 항상 똑같이 해야 되는 걸 우리가 할 필요가 있나?라는 의문이 들게 하는 코드입니다.
this가 뭔지 많은 사람들이 해깔려 한다.
자바스크립트를 공부하다 보면 this가 많이 나오는데 클래스를 사용하다 보면 많이 보입니다. 위에서 보는 것처럼 this에 bind를 해야 class 관련된 함수와 props등에 접근할 수 있습니다. 대부분 bind를 해줘야 하고 하는 것과 안 하는 것에 큰 퍼포먼스 차이가 없다면 그냥 자동으로 해주면 안 되냐. 물론 () =>를 사용하면 자동으로 되긴 합니다만 어쨌든 엑스트라인 거죠.
HOC 헬
리액트를 쓰다 보면 자주 쓰는 로직을 따로 빼내서 사용할 필요가 있는데 그전까지는 HOC(High order component)를 사용했었습니다.
그런 것이 한두 개면 괜찮은데 여러 개가 겹치다 보면 HOC 헬을 경험할 수 있습니다. 자바 스크립트에는 뭐 이리 헬이 많은지.
먼저 위에 말한 것처럼 HOC 헬을 한번 보겠습니다. 만약에 SomeComponent가 마우스 포지션, window 크기, 유저 위치 정보등에 정보가 필요하다면 아래와 같이 HOC로 감싸줄 수 있습니다. 이렇게 되다 보면 일단 그렇게 보기도 좋지 않을뿐더러 나중에 element를 찾기도 힘들어질 수 있습니다. 리액트 데브 툴을 보면 엄청 더러워 보이게 됩니다. 하지만 훅이 없이 함수형 컴포넌트에서는 라이프 사이클과 state을 사용할 수 없을 때 사용했던 패턴 중 하나입니다.
const HocHell = () => {
return (
<Hoc1>
<WithMousePosition>
<WithWindowSize>
<WithUserLocation>
<SomeComponent />
</WithUserLocation>
</WithWindowSize>
</WithMousePosition>
</Hoc1>
);
};
하지만 만약에 Hook을 사용해서 SomeComponet를 render 해주게 되면 어떻게 될까요?
const WithHook = () => {
const mousePosition = useMousePosition()
const windowSizes = useWindowSize()
const userLocation = useUserLocation()
return (
<SomeComponent
mousePosition={mousePosition}
windowSizes={windowSizes}
userLocation={useLocation}
>
)
}
위에서 보는 것처럼 코드가 훨씬 간결해 보이게 됩니다. 보시는 것처럼 보통 훅은 use를 붙여서 이름을 지어 줍니다.
리액트 훅을 사용하면 좋은 마지막으로 정리
- 코드가 간결해진다.
- 가독성이 좋아진다.
- 많은 라이브러리들도 훅으로만 나오고 있다.
- HOC 헬을 벗어 날 수 있다.
- 불필요한 것 같은 코드를 적을 필요가 없다.
이제는 사실 클래스 컴포넌트를 사용해서 컴포넌트를 만들 이유가 많이 없어졌습니다. 앞에서 봤던 것 처럼 Hook이 써야 되는 코드도 많이 줄어들게 되고 보고 읽기도 대부분 더 편하기 때문입니다. 맨 처음 리액트 훅이 나왔을 때 같이 일하는 사람들 중에 거부감을 느끼는 사람들도 있었지만 사용하고 익숙해지다 보니 지금은 클래스 컴포넌트가 생각도 안 난다고 합니다. 또한 많은 npm 라이브러리도 hook을 위해서 만들어지는 것도 많고 훅으로 만들어지는 경우도 많이 있습니다. Hook 자체를 클래스 컴포넌트에 사용할 수 없기 때문에 필요한 라이브러리가 hook이라면 클래스 컴포넌트를 함수형으로 바꿔줘야 하거나 Wrapper를 만들어서 훅을 감싸줘야 합니다.
'Reactjs' 카테고리의 다른 글
react-native-vector-icons 설치하는 방법 및 사용방법 (0) | 2022.12.19 |
---|---|
Next js로 API 만들어 사용하기(API routes) (2) | 2022.12.10 |
리액트 훅(React hook)이란? 예제로 알아보기 (0) | 2021.05.19 |