안녕하세요.
저희팀에서 자체적으로 만들어 유용하게 사용하고 있는 useDeepEffect 커스텀 훅을 소개하겠습니다.
(결국 구글링을 통해 알아낸 코드입니다 ㅎㅎ)
보통 리액트에서 작업시 부모컴포넌트에서 자식컴포넌트로 props를 넘기는 경우가 진짜 많은데요!
그런 경우에서도 자식 컴포넌트에서 useEffect를 사용한다면 useDeepEffect는 필수적이라고 생각합니다.
useDeepEffect 사용예시
간단한 예시 코드 입니다.
import { useDeepEffect } from '@hooks/useDeepEffect';
import React, { useEffect, useState } from 'react';
function Parents() {
const [name, setName] = useState('이상민');
const [email, setEmail] = useState('hwon3794@gmail.com');
const [count, setCount] = useState<number>(0);
useEffect(() => {
setInterval(() => setCount(prev => prev + 1), 500);
}, []);
return (
<div>
<div>{name}</div>
<div>{email}</div>
<div>{count}</div>
<button onClick={() => setName(prev => `${prev}+`)}>이름 바꾸기</button>
<button onClick={() => setEmail(prev => `${prev}+`)}>메일 바꾸기</button>
<ChildUseEffect data={{ name, email }} count={count} />
<ChildUseDeepEffect data={{ name, email }} count={count} />
</div>
);
}
export default Parents;
function ChildUseEffect({ data, count }) {
useEffect(() => {
console.log('계속 리렌더링된다~~');
}, [data]);
return null;
}
function ChildUseDeepEffect({ data, count }) {
useDeepEffect(() => {
console.log('데이터가 바뀔 때만 리렌더링된다~~');
}, [data]);
return null;
}
간단한 코드이지만 설명을 짧게 한다면 부모컴포넌트에서 0.5초 마다 count up을 하고
자식 컴포넌트에게 data와 함께 전달하는 코드입니다.
자식컴포넌트들은 코드와 같이 data가 변화가 될때마다 리렌더링을 하고 싶은데요!
하지만 그냥 useEffect를 사용한 자식 컴포넌트는 0.5초 마다 리렌더링됩니다...
(잠깐 글을 작성하는 동안에도 꽤 많이 리렌더링되고 있네요!)
반대로 useDeepEffect는 name과 email 버튼을 클릭해 상태가 바뀔때만 리렌더링 됩니다!
(이름바꾸기 버튼을 한번 눌렀을 때 한번 렌더링이 되네요!)
useDeepEffect 코드
import { isEqual } from 'lodash';
import { useEffect, useRef } from 'react';
export const useDeepEffect = (fn: any, deps: any[]) => {
const isFirst = useRef(true);
const prevDeps = useRef(deps);
useEffect(() => {
const isFirstEffect = isFirst.current;
const isSame = prevDeps.current.every((obj, index) => isEqual(obj, deps[index]));
isFirst.current = false;
prevDeps.current = deps;
if (isFirstEffect || !isSame) {
return fn();
}
}, [deps, fn]);
};
코드 설명
- isFirstHook을 사용하여 참조를 선언 useRef하여 효과의 상태를 추적합니다. 이것이 처음 실행되어야 하는지 확인하기 위해 나중에 필요합니다. prevDepsHook이 검토해야 하는 이전 종속성에 대한 참조를 항상 갖도록 ref 와 동일한 작업을 수행합니다 .
- 이 방법을 사용 Array.propotype.every하여 현재 종속성 배열을 반복하고 각각을 이전 값과 비교합니다. 결과를 변수에 저장하여 변경된 사항이 있는지 식별합니다.
- 효과 기능을 실행할지 말지 결정할 때입니다. 처음이거나 isSame변수가 false인 경우에만 실행되어야 합니다. 이는 이전 종속성에서 무언가 변경된 것을 의미합니다.
- 결국 Hook이 처음 실행되지 않도록 해야 합니다. prevDeps또한 방금 비교에 사용한 마지막 종속성으로 참조 를 업데이트해야 합니다.
마무리..
제가 첫번째 직장에서 프론트엔드 작업을 하면서 useEffect를 사용한 로직처리가 많이 발생하게 되었고
useEffect를 1~3개이상 사용하게 되면서 성능이 안좋아지는걸 경험했습니다.
그때부터 습관적으로 log를 찍어보면서 불필요한 리렌더링을 줄이려고 했고 그런 과정에서 알게된 커스텀 훅입니다.
지금도 아주 유용하게 사용중이고 여러분들도 사용해보세요!
'개발' 카테고리의 다른 글
[velog 2022-06-27] NodeJS 레디스(Redis) 사용하기 (0) | 2024.05.23 |
---|---|
[velog 2022-06-21] NextJS 서버사이드 301 리다이렉트(redirect) (0) | 2024.05.23 |
[velog 2022-06-17] 아이웨딩(회사) React(CSR) -> NextJS 전환 작업 (0) | 2024.05.23 |
[velog 2022-06-14] React zustand 상태 관리 라이브러리 사용하기 (0) | 2024.05.23 |
[velog 2022-06-10] React 부모 새창(window.open) data refresh 커스텀훅 (0) | 2024.05.23 |