React: useEffect()

summereuna🐥·2022년 2월 21일
0

React JS

목록 보기
12/69

React의 state, prop을 배웠고 이제는 Effect를 알아보자.

Intro: useEffect()가 필요한 이유


ReactJS가 어떻게 작동했는지 상기해보기 위해 예시를 하나 보면서 시작하자.

예시

counter로 예시 하나를 만들어 보자.

import Button from "./Button";
import styles from "./App.module.css";
import { useState } from "react";

function App() {
  //counter
  //리액트 앱을 이용하고 있기 때문에 React.useState();라고 할 필요 없다. useState만 import하면 된다.(이것도 자동으로 됨)
  const [counter, setValue] = useState(0);
  //useState()는 반환값(return 값)으로 array를 주는 걸 알고 있음!
  //클릭되면 실행될 함수를 만든다. 클릭하면 setValue의 인자로 현재 값 +1을 보내도록 설정한다.
  const onClick = () => setValue((prev) => prev + 1);
  //그러고 나서 컴포넌트가 렌더될 때 콘솔로그 찍어보자!
  console.log("render");
  return (
    <div>
      <h1 className={styles.title}>Counter: {counter}</h1>
		//버튼
      <button onClick={onClick}>Click Me</button>
      <Button text={"HEY"} />
    </div>
  );
}

export default App;

컴포넌트가 렌더될 때 콘솔로그가 이렇게 찍힌다.

이처럼 state가 변할 때 마다 매번 console.log가 실행되는 것을 볼 수 있다.
즉, 새로운 값으로 증가하는 거 말고는 버튼을 누르면 똑같은 펑션이 반복적으로 실행된다.

useEffect()가 필요한 이유

가끔은 계속 다시 render될 때 마다 반복 실행되어도 괜찮은 코드가 있을 수도 있다.
하지만 component가 처음 render될 때만 코드가 실행되길 원할 수도 있다.

처음 render에만 코드가 실행되고 다른 state변화에는 코드가 실행되지 않도록 하는 것! 바로 그게 effect이다.
예) API 통해 데이터 가져올 때, 첫 번째 component render에서 API를 call하고, 이후에 state가 변할 때는 API에서 데이터를 또 다시 가져오고 싶진 않을 거임!

예시

import Button from "./Button";
import styles from "./App.module.css";
import { useState } from "react";

function App() {
  //counter
  const [counter, setValue] = useState(0);
  const onClick = () => setValue((prev) => prev + 1);
  //여기서 API를 call했다고 생각해 보자.
  console.log("Call an API");
  return (
    <div>
      <h1 className={styles.title}>Counter: {counter}</h1>
      <button onClick={onClick}>Click Me</button>/>
    </div>
  );
}

export default App;


API 통해 데이터 가져올 때, 첫 번째 component render에서 API를 call하고, 이후에 state가 변할 때는 API에서 데이터를 또 다시 가져오고 싶진 않은데 위 처럼 코드를 작성하면 계속 api를 호출하는 형태이다.

이를 해결하기 위해 effect가 필요하다.

요약


  • state를 변경하면 항상 App() 컴포넌트 내의 모든 code가 다시 실행된다.
  • 어떻게 컴포넌트 내의 특정 코드가 component render시 처음 한 번 만 실행되게 할 수 있을까? 즉, 특정 코드의 실행을 제한 하여 나중에 state가 변해도 그 특정 코드(state 변하는 부분 말고 다른 부분들)가 실행되지 않도록 하는 방법은?

useEffect(effect, deps)


component가 처음에 render할 때만 실행되게 하고 그 다음 부터는 실행되지 않도록 제한하자.

useEffect라고 불리는 function을 사용하면 된다.

useEffect import하기

import { useState, useEffect } from "react";

useEffect는 두 가지 param을 가진 함수이다.

useEffect(effect, deps)

1. @param effect

정리 함수를 반환할 수 있는 명령 함수를 첫 번째 파라미터로 보내면 된다.
즉, 첫 번째 param인 effect에는 딱 한번만 실행하고 싶은 코드를 담은 함수를 보내자.

예시

import styles from "./App.module.css";
import { useState, useEffect } from "react";

function App() {
  //counter
  const [counter, setValue] = useState(0);
  const onClick = () => setValue((prev) => prev + 1);
  //state가 변할 때 마다 매번 실행되는 것
  console.log("I run all the time.");
  //처음 렌더될 때만 실행될 함수
  const iRunOnlyOnce = () => {
    console.log("I run only once.");
  };
  //🔥첫 번째 인자로 처음 랜더될 때만 실행할 함수를 보내고, 두 번째 인자로는 빈 어레이를 보낸다.
  useEffect(iRunOnlyOnce, []);
  return (
    <div>
      <h1 className={styles.title}>Counter: {counter}</h1>
      <button onClick={onClick}>Click Me</button>
    </div>
  );
}

export default App;
  • 결과

좀 더 간단히 보기 위해 파라미터에 바로 익명 함수를 작성해 보자.

import styles from "./App.module.css";
import { useState, useEffect } from "react";

function App() {
  //counter
  //리액트 앱을 이용하고 있기 때문에 React.useState();라고 할 필요 없다. useState만 import하면 된다.(이것도 자동으로 됨)
  const [counter, setValue] = useState(0);
  //useState()는 반환값(return 값)으로 array를 주는 걸 알고 있음!
  //클릭되면 실행될 함수를 만든다. 클릭하면 setValue의 인자로 현재 값 +1을 보내도록 설정한다.
  const onClick = () => setValue((prev) => prev + 1);
  //여기서 API를 call했다고 생각해 보자.
  console.log("I run all the time.");
  //첫 번째 인자로 처음 랜더될 때만 실행할 함수를 보낸다. (익명함수로 걍 콘솔로그 보냄)
  //두 번째 인자로는 빈 어레이를 보낸다.
  useEffect(() => {
    console.log("✅ CALL THE API...");
  }, []);
  return (
    <div>
      <h1 className={styles.title}>Counter: {counter}</h1>
      <button onClick={onClick}>Click Me</button>
    </div>
  );
}

export default App;
  • 결과: 보다시피 "✅ CALL THE API..."는 한 번만 실행되고 재실행되지 않는다.

이처럼 API를 딱 한번만 호출하고 싶을 때 같은 경우에 useEffect를 사용하면 유용하다!

2. @param deps

두 번째 파라미터인 deps가 존재하는 경우, 목록의 값이 변경되는 경우에만 effect가 활성화된다.

예시

search bar를 만들어서 input에 value 입력시 state 변하는 거 연습해보자.

  1. useState() 만들기, (처음 디폴트 값은 아무 것도 없으니 ""로 작성하면 된다.)
    const [keyword, setKeyword] = useState("")

  2. input 만들기
    <input type="text" placeholder="Search here..." />

  3. input에 event listener 연결
    <input onChange={onChange} type="text" placeholder="Search here..." />

  4. 이벤트 시 발생하는 핸들러 작성, 인자로 event 받아서 모디파이어 업데이트 할 때 사용
    그러면 이벤트를 발생시킨 input에서 value 받아와서 'keyword' state에 업데이트함
    const onChange = (event) => setKeyword(event.target.value);

  5. 업데이트 된 state의 keyword를 가져와 input의 value 값에 넣어주기 위해 input의 value 값은 다음과 같이 작성하면 된다.
    <input value={keyword} ...

결과

  • 타이핑할 때 마다(인풋에 밸류 값을 입력할 때 마다) state가 modify되기 때문에 콘솔에 "I run all the time."이 찍히는 것을 확인할 수 있다.

한 글자 한 글자 타이핑할 때 마다 새로 호출되는건 별로다..^3^

  • 콘솔로그 추가해서 지금 상태가 어떤지 한 번 보자.
import Button from "./Button";
import styles from "./App.module.css";
import { useState, useEffect } from "react";

function App() {
  //👉1. counter
  const [counter, setValue] = useState(0);
  const onClick = () => setValue((prev) => prev + 1);
  
  //🔥🔥그냥 코드 재실행 될때 마다 찍히는 로그
  console.log("I run all the time.");
  
  //👉2. 검색 API
  const [keyword, setKeyword] = useState("");
  const onChange = (event) => setKeyword(event.target.value);
  //🔥처음 렌더 때만 찍히는 로그
  useEffect(() => {
    console.log("✅ CALL THE API...");
  }, []);
  //🔥🔥검색 API state 변할 때 마다 찍히는 로그
  console.log("SEARCH", keyword);
  return (
    <div>
      <input
        value={keyword}
        onChange={onChange}
        type="text"
        placeholder="Search here..."
      ></input>
      <h1 className={styles.title}>Counter: {counter}</h1>
      <button onClick={onClick}>Click Me</button>
    </div>
  );
}

export default App;

이렇게 작성하고 실행하면 문제가 좀 있다.
뭐가 됐든 재실행될 때 마다 찍히는 로그는 "I run all the time."이고,
검색 API state 변할 때 마다 찍히는 로그가 "SEARCH", keyword로 설정을 해 뒀는데,
카운터 버튼을 클릭해도 콘솔에 "I run all the time."뿐만 아니라 "SEARCH", keyword가 뜬다.
이말인 즉슨, 현재 counter가 변할 때도 검색API 실행되고 있다.
search keyword에 변화가 있을 때만 검색API 사용되게 하기 위해서는 어떻게 해야 할까?

내 코드의 특정한 부분만 변화했을 때, 원하는 코드만 실행 할 수 있는 방법은?
즉, movie state가 변화할 때만 user가 원하는 영화를 검색하는 방법은?

useEffect()의 deps를 사용해 예시 코드 수정

  1. 일단 검색 API state 변할 때 마다 찍히는 로그를 useEffect() 안에 넣어 주자.
    그런데 이렇게만 해 두면 처음 랜더 때만 딱 한번 실행되기 때문에 검색을 다시 못한다.
  //검색 API state 변할 때 마다 찍히는 로그를 useEffect() 안에 넣어 주자.
  useEffect(() => {
    console.log("SEARCH", keyword);
  }, []);
  1. useEffect()의 deps를 사용하여 코드의 특정한 부분이 변화했을 때, 원하는 코드만 실행 할 수 있게 해보자.
    keyword가 변할 때 코드를 실행하고 싶으면 어레이 안에 keyword를 넣어 주면 된다.
  useEffect(() => {
    console.log("SEARCH", keyword);
  }, [keyword]);

이제 counter state가 변해도 console.log("SEARCH", keyword)는 실행되지 않는다.
어레이에 keyword를 입력하면 keyword가 변할 때 마다 console.log("SEARCH", keyword);가 실행된다.

이런 이유로 useEffect()의 두 번째 인자에 빈 어레이를 넣으면 코드가 한 번만 실행되는 거였다.
지켜볼 친구가 없기 때문에 ㅇㅇ!

결과

  • 근데 아직 완벽하다고 할 수 없는 것이 아직 검색 안 했는데도 콘솔 라인 3번째에 보면 SEART 해서 검색 API가 실행되고 있다.

if 문 사용해서 keyword가 있을 때만 실행되게 하자.

//keyword가 빈 값이 아니라면 keyword 검색하도록
useEffect(() => {
    if (keyword !== "") {
      console.log("SEARCH", keyword);
    }
  }, [keyword]);

결과

keyword.length가 5보다 길 때 검색되게 하는 조건도 추가해보자.

//keyword가 빈 값이 아니고, 길이가 5보다 길 때 keyword 검색하도록
useEffect(() => {
    if (keyword !== "" && keyword.length > 5) {
      console.log("SEARCH", keyword);
    }
  }, [keyword]);

요약

이제 언제 코드가 실행될지 결정하는 방법을 배웠다!
- component가 생성되는 첫 시작점에서만 코드 실행되게 하기

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

- keyword 값이 변할 때만 코드 재실행되게 하기

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

- keyword 값 또는 counter 값이 변할 때 코드 재실행되게 하기

useEffect(() => {
    console.log("I run when 'keyword & counter' changes.");
  }, [keyword, counter]);
profile
Always have hope🍀 & constant passion🔥

0개의 댓글