[React] useEffect 올바른 곳에 쓰기

olwooz·2023년 2월 6일
0

React

목록 보기
8/8

useEffect

useEffect is a React Hook that lets you synchronize a component with an external system.

React Docs에 쓰여 있는 useEffect의 정의다.
useEffect는 컴포넌트를 외부 시스템과 동기화하기 위해 사용하는 hook이라고 한다.

useEffect가 필요 없는 상황

React Docs - You Might Not Need An Effect에 따르면, 불필요하게 useEffect를 사용하는 상황은 크게 두 가지가 있다.

1. 데이터 변경 (e.g. state)
2. 이벤트 핸들링 (e.g. API request)

useEffect를 사용하지 말아야 할 곳에서 사용하는 실수들과 개선 방법을 알아볼 것이다.

1. 데이터 변경

To-do list 예제이다.

function TodoList({ todos, filter }) {
  const [newTodo, setNewTodo] = useState('');
  const [visibleTodos, setVisibleTodos] = useState([]);
  
  useEffect(() => {
    setVisibleTodos(getFilteredTodos(todos, filter));
  }, [todos, filter]);

  // ...
}

위와 같이 visibleTodos라는 state를 만들어서 useEffect를 통해 todofilter가 바뀔 때마다 변경하는 것은 불필요하고 비효율적이다. todosfilter가 바뀌면 1차로 렌더링이 일어나고, visibleTodos가 바뀌면서 렌더링이 또 일어나게 된다.
다시 말해, 가지고 있는 propsstate를 사용해 만들 수 있는 값이면, 또 다른 state를 만들 필요가 없다는 것이다.

function TodoList({ todos, filter }) {
  const [newTodo, setNewTodo] = useState('');
  
  const visibleTodos = getFilteredTodos(todos, filter);
  
  // ...
}

이런 식으로 코드를 바꾸면 코드가 간결해질 뿐만 아니라 성능도 개선된다.
만약 getFilteredTodos() 함수가 오래 걸리는 함수라면 useMemo를 통해 추가적인 개선이 가능하다.

import { useMemo, useState } from 'react';

function TodoList({ todos, filter }) {
  const [newTodo, setNewTodo] = useState('');

  const visibleTodos = useMemo(() => getFilteredTodos(todos, filter), [todos, filter]);
  // ...
}

2. 이벤트 핸들링

로그인 Form 예제이다.

function Form() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [isSubmitting, setSubmitting] = useState(false);
  const [userInfo, setUserInfo] = useState(null);
  
  useEffect(() => {
    (async () => {
      if (!isSubmitting || !email || !password) return;
      
      const user = await loginAPI({email, password});
      
      setUserInfo(user);
      setSubmitting(false);
    })();
  }, [isSubmitting]);
  
  return (
    <Form>
      // ...
      <SubmitButton 
        type="submit" 
        onClick={() => setSubmitting(true)}
      >
        {isSubmitting ? "Loading..." : "Sign In"}
      </SubmitButton>
    <Form>
  );
}

버튼 클릭 이벤트가 isSubmitting을 true로 만들고, 그로 인해 useEffect가 호출되어 로그인 API를 호출하는 코드이다. 이 경우에는 useEffect 내부에 위치한 로그인 관련 로직이 이벤트와 직접적으로 연관된 로직이기 때문에 버튼 클릭 이벤트 핸들러 자체에서 실행되어야 한다.

// useLogin.js
export function useLogin() {
  const [isLoading, setLoading] = useState(false);
  
  const login = async (email, password) => {
    if (!email || !password) return;
    
    setLoading(true)
    const user = await loginAPI({email, password});
    setLoading(false);
    
    return user
  };
      
  return [login, isLoading];  
}
// Form.jsx
import { useLogin } from './useLogin';

function Form() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  
  const [login, isSubmitting] = useLogin();
  
  return (
    <Form>
      // ...
      <SubmitButton 
        type="submit" 
        onClick={() => login(email, password)}
      >
        {isSubmitting ? "Loading..." : "Sign In"}
      </SubmitButton>
    <Form>
  );
}

로직을 어디에 위치시킬 지 판단하는 방법은 간단하다. 로직이 특정 인터랙션으로 인해 발생한다면 이벤트 핸들러에 넣어야 하고, 유저가 스크린에서 컴포넌트를 봄으로 인해 발생한다면 useEffect에 위치해야 한다.

0개의 댓글