이론 (Front-end)/React

[React] useEffect

이우열 2023. 7. 16. 17:12
728x90

create-react-app을 통해 프로젝트를 생성하고

useState를 작성하면 자동으로 react에 있는 useState를 import 해줍니다.

 

import { useState } from "react";

function App() {
  const [counter, setValue] = useState(0);
  const onClick = () => {
    setValue((prev) => prev + 1);
  };
  return (
    <div>
      <h1>{counter}</h1>
      <button onClick={onClick}>click me</button>
    </div>
  );
}

export default App;

counter를 만들고 버튼을 클릭하면 counter가 증가하는 로직을 작성해봅시다.

 

 

 

function App() {
  const [counter, setValue] = useState(0);
  const onClick = () => {
    setValue((prev) => prev + 1);
  };
  console.log("render");
  return (
    <div>
      <h1>{counter}</h1>
      <button onClick={onClick}>click me</button>
    </div>
  );
}

중간에 컴포넌트가 렌더링될 때마다 콘솔 로그에 "render"를 출력해봅시다.

 

"render"는 버튼을 클릭해 counter를 증가시킬 때마다 계속 출력됩니다.

 

 

하지만 우리는 처음 렌더링했을 때 한 번만 실행하고 state가 변해도 실행되지 않게 하고 싶습니다.

 

예를 들면, API를 통해 데이터를 가져왔을 때 데이터가 수정되면 다시 API를 호출해서 데이터를 가져오지 않게 하고 싶습니다.

 


 

✅ useEffect

컴포넌트가 처음 렌더링될 때 딱 한 번만 실행하기 위해서 사용하는 함수

 

useEffect두 개의 인자(argument)를 가집니다.

 

 

✏️ 첫 번째 인자

딱 한 번만 실행할 코드

 

import { useState, useEffect } from "react";

function App() {
  const [counter, setValue] = useState(0);
  const onClick = () => {
    setValue((prev) => prev + 1);
  };
  console.log("i run all the time");
  const iRunOnlyOnce = () => {
    console.log("i run only once");
  };
  useEffect(iRunOnlyOnce, []);
  return (
    <div>
      <h1>{counter}</h1>
      <button onClick={onClick}>click me</button>
    </div>
  );
}

export default App;

useEffect를 import하고

useEffect 안에 첫 번째 인자한 번만 실행할 함수두 번째 인자빈 배열을 작성해줍니다.

 

버튼을 클릭하면 컴포넌트가 리렌더링되고 "i run all the time"렌더링될 때마다 출력됩니다.

하지만 useEffect를 사용한 iRunOnlyOnce 함수는 처음 렌더링될 때만 실행되기 때문에 "i run only once"한번만 출력됩니다.

 

 

✏️ 화살표 함수로 축약

function App() {
  const [counter, setValue] = useState(0);
  const onClick = () => {
    setValue((prev) => prev + 1);
  };
  console.log("i run all the time");
  useEffect(() => {
    console.log("CALL THE API...");
  }, []);
  return (
    <div>
      <h1>{counter}</h1>
      <button onClick={onClick}>click me</button>
    </div>
  );
}

위와 같이 useEffect 안에 바로 화살표 함수를 사용하여 코드를 축약할 수 있습니다.

 

 

✏️ 두 번째 인자

dependencies, React.js가 지켜보아야 하는 것들
두 번째 인자로 작성한 것들이 변할 때 React.js가 코드를 실행시킴

 

import { useState, useEffect } from "react";

function App() {
  const [counter, setValue] = useState(0);
  const [keyword, setKeyword] = useState("");
  const onClick = () => {
    setValue((prev) => prev + 1);
  };
  const onChange = (event) => {
    setKeyword(event.target.value);
  };
  console.log("i run all the time");
  useEffect(() => {
    console.log("CALL THE API...");
  }, []);
  console.log("SEARCH FOR", keyword);
  return (
    <div>
      <input
        value={keyword}
        onChange={onChange}
        type="text"
        placeholder="Search here..."
      />
      <h1>{counter}</h1>
      <button onClick={onClick}>click me</button>
    </div>
  );
}

export default App;

 

검색을 위해 input 태그를 작성하고 valuekeyword state를 연결해줍니다.

value 값이 변경될 때마다 onChange 함수를 호출하여 keyword의 값을 event.target.value로 바꿔줍니다.

 

위의 결과처럼 "SEARCH FOR"가 버튼을 클릭해도 출력되는 것을 확인할 수 있습니다.

하지만 이 코드는 keyword가 변경될 때만 실행되게 하고 싶습니다.

즉, counter가 변하면 실행되지 않지만 keyword가 변했을 경우만 실행하는 것입니다.

 

 

이 때, useEffect두 번째 인자를 사용하면 됩니다.

useEffect(() => {
  console.log("SEARCH FOR", keyword);
}, [keyword]);

위와 같이 코드를 작성하면 keyword가 변했을 경우만 실행됩니다.

첫 번째 인자를 작성했던 예제에서 빈 배열을 사용한 것은 React.js지켜볼 것이 없기 때문에 처음 한 번만 실행된 것입니다.

 

 

하지만 문제는 처음 시작할 때도 실행이 된다는 점입니다.

useEffect(() => {
  if (keyword !== "" && keyword.length > 5) {
    console.log("SEARCH FOR", keyword);
  }
}, [keyword]);

이렇게 조건문을 추가하여 keyword가 조건을 만족했을 경우만 실행되게 할 수 있습니다.

 

 

// 1
useEffect(() => {
  console.log("I run only once");
}, []);

// 2
useEffect(() => {
  console.log("I run when 'keyword' changes");
}, [keyword]);

// 3
useEffect(() => {
  console.log("I run when 'counter' changes");
}, [counter]);

위의 코드처럼 언제 코드가 실행될지 결정하는 방법을 알아봅시다.

 

1번의 경우는 빈 배열을 인자로 넣었기 때문에 컴포넌트 생성시 처음 한 번만 실행됩니다.

2번의 경우는 keyword가 바뀔 때마다 실행되고

3번의 경우는 counter가 바뀔 때마다 실행됩니다.

 

 

✏️ Cleanup function

component가 파괴될 때 실행하는 함수를 Cleanup function이라고 함
예를 들어, component가 없어질 때 어떤 분석 결과를 보내는 경우, event listener를 지우거나 console.log에 무언가를 보여주는 경우

 

import { useState, useEffect } from "react";

function Hello() {
  function byeFn() {
    console.log("bye :(");
  }
  function hiFn() {
    console.log("hi :)");
    return byeFn;
  }

  React.useEffect(hiFn, []);

  return <h1>Hello</h1>;
}

function App() {
  const [showing, setShowing] = React.useState(false);
  const onClick = () => setShowing((prev) => !prev);

  return (
    <div>
      {showing ? <Hello /> : null}
      <button onClick={onClick}>{showing ? "Hide" : "Show"}</button>
    </div>
  );
}

export default App;

 

React.js가 지켜볼 dependency가 없기 때문에 component가 생성될 때 한 번만 실행됩니다.

 

component파괴될 때 function을 실행하고 싶다면 hiFn의 return에 함수를 작성합니다.

 

위의 결과처럼 컴포넌트가 생성될 때 처음 한 번 hiFn이 실행되어 console.log에 "hi :)"가 출력되고

Hide 버튼을 통해 컴포넌트를 파괴하면 byeFn이 실행되어 console.log에 "bye :("가 출력됩니다.

 

 

✏️ 다양한 방법으로 cleanup function 사용

function Hello() {
  // 1. 함수를 두 개 따로 작성해서 사용
  function byeFn() {
    console.log("bye :(");
  }
  function hiFn() {
    console.log("hi :)");
    return byeFn;
  }
    
  useEffect(hiFn, []);
    
  // 2. 일반 함수 사용
  useEffect(function () {
    console.log("hi :)");
    return function () {
      console.log("bye :(");
    };
  }, []);

  // 3. 화살표 함수 사용
  useEffect(() => {
    console.log("hi :)");
    return () => {
      console.log("bye :(");
    };
  }, []);

  return <h1>Hello</h1>;
}

 

1번의 경우 함수를 따로 따로 작성하여 사용합니다.

2번의 경우는 useEffect바로 함수를 작성하고 return에도 바로 함수를 작성합니다.

3번의 경우는 화살표 함수를 사용하여 코드를 좀 더 축약합니다.

 

 

✅ Memo VS useEffect 비교

 

Memoprops가 변경되지 않았다면 컴포넌트를 리렌더링하지 않도록 해주는 것입니다.

useEffectprops가 변경되거나 컴포넌트의 생명주기의 처음과 마지막에 한 번 실행하는 함수입니다.

728x90