[React.js] 리액트의 useEffect 활용법 & 성능 최적화 방법

2021. 7. 19. 00:14Web_Programming/React

 

 

💻 useEffect 활용법

 

❗ 의존성 배열은 잘못 입력 시, 버그로 이어질 수 있어 되도록 사용하지 않는 것을 권장

 

💡 useEffect 내에서 사용되는 값은 의존성배열에 추가해주어야 변경사항이 적용됨.

function Profile({ userId }) {
  const [user, setUser] = useState();
  useEffect(() => {
    fetchUser(userId).then(data => setUser(data));
  }, [userId]);
}

 

💡 마운트 시점에만 실행되기 원한 다면 별도의 훅으로 사용하면 더 직관적이라는 장점이 있습니다.

function Profile({ userId }) {
  const [user, setUser] = useState();
  useOnMounted(() => fetchUser(userId).then(data => setUser(data)))
}

 

💡 useEffect 내에서 async/await 함수 사용

useEffect는 항상 함수 를 반환해야하기 때문에 별도의 함수를 정의하여 사용합니다.

function Profile({ userId }) {
  const [user, setUser] = useState();
  async function fetchAndSetUser () {
    const data = await fetchUser(userId);
    setUser(data);
  }

  useEffect(() => {
    fetchAndSetUser();
  }, [fetchAndSetUser]);
  
  return (...);
}

 

💡 의존성 배열 사용을 줄이기 위한 방법

  • if문으로 호출 조절
function Profile({ userId }) {
  const [user, setUser] = useState();
  async function fetchAndSetUser() {
    const data = await fetchUser(userId);
    setUser(data);
  }

  useEffect(() => {
    if(!user || user.id !== userId) {
      fetchAndSetUser();
    }
  });
  
  // ...
}
  • 상태값 변경함수의 매개변수로 함수를 사용
function MyComponent() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    function onClick() {
      setCount(prev => prev + 1);
    }
    window.addEventListener('click', onClick);
    return () => window.removeEventListener('click', onClick);
  });
  // ...
}
  • useReducer 훅 사용
function Timer({ initialTotalSeconds }) {
  const [state, dispatch] = useReducer(reducer, {
    hour: initialTotalSeconds / 3600
    minute: (initialTotalSeconds % 3600) / 60
    second: initialTotalSeconds % 60
  });
  const { hour, minute, second } = state;
  useEffect(() => {
    const id = setInterval(dispatch, 1000);
    return () => clearInterval(id);
  });
  //...
}

function reducer(state) {
  const { hour, minute, second } = state;
  if(second) {
    return { ..state, second: second - 1 };
  } else if(second) {
    return { ..state, minute: minute - 1, second: 59 };
  } else if(second) {
    return { ..state, hour: hour - 1, minute: 59, second: 59 };
  } else {
    return state;
  }
}
  • userRef 훅 사용
function MyComponent({ onClick }) {
  const onClickRef = useRef();
  
  // concurrent mode를 위한 작성법
  useEffect(() => {
    onClickRef.current = onClick;
  });
  
  useEffect(() => {
    window.addEventListener('click', () => {
      onClickRef.current();
      // ...
    })
    // ...
  });
  // ...
}

 

 

💻 렌더링 속도를 올리기 위한 성능 최적화 방법

 

리액트는 데이터+컴포넌트 함수로 화면을 그립니다.

여기서 대부분의 연산은 "컴포넌트 함수 실행과 가상돔"에서 발생합니다.

 

+가상돔

이전 돔과 비교하여 변경된 부분만 렌더링하기 위한 가상의 돔

 

💡 React.memo

 

리액트 memo함수는 속성값이 변경될 경우에만 렌더링 하도록 도와주는 함수입니다.

 

memo함수는 첫번째인자로 컴포넌트, 두번째 인자로 비교함수를 받습니다.

 

두번째 인자가 없을시, 얕은 비교 수행 함수가 실행됩니다.

👉 얕은 비교라도 속성값이 변경되지 않았다면 실제 돔에도 변경되지 않아 큰문제는 없습니다.

function MyComponent(props) {
  // ...
}
function isEqual(prefProps, nextProps) {
  // true 또는 false를 반환
}
React.memo(MyComponent, isEqual)

 

이전 속성값과 상태값에 대해 포스팅 했을때,

불변값이 속성값과 가변값 상태값을 말했었습니다.

여기서 상태값 또한 불변값으로 관리하는 것이 좋다고 말했던 이유가 나오는데요.

 

값을 이전과 비교할떄 ===연산자로 비교하기 위해 객체를 불변객체로 관리하는것이 좋기 때문입니다. 

const state = {};
const newState = {};

state === {...state, ...{name: 'horong'}}; // false

 

 

💡 함수 속성값

 

❗ 속성값으로 함수를 자식에게 넘겨줄 시, React.memo를 사용하였다 할 지라도

함수는 부모가 렌더링 될 떄마다 새로 생성되기 때문에 자식 또한 다시 렌더링됩니다.

 

👉 useCallback 훅 사용으로 해결

function Parent() {
  const [v1, setV1] = useState();
  
  const onChangeCallback = useCallback(v => {
    // ...
    setV1(v);
  }, [])
  
  return <Child onChange={onChangeCallback}>
}

 

💡 객체 속성값

 

❗ 객체 또한 함수와 동일하게 매번 새로운 객체로 인식합니다. 

 

👉 불변객체라면 분리하여 작성

function Parent() {
  return <Child data={DATA}>
}

const DATA = [1, 2, 3];

👉 가변객체라면 useMemo 훅 사용 ( :필요할 때만 변경)

function Parent() {
  const [maxValue, setMaxValue] = useState(0);
  const data = useMemo(() => DATA.filter(e => e<=maxValue), [
    maxValue
  ]);
  return <Child data={data}>
}

const DATA = [1, 2, 3];

 

 

 

 

반응형