[React][자료] useEffect Hook :: Higher의 창작소

 

higher77.tistory.com

해당 글은 위의 자료를 보충 설명하기 위한 자료입니다. 

 

 

 

 


 

1. Side Effect

더보기
더보기
더보기

1. Side Effect Example


예제1

function printNum(number){
  console.log(number); //side Effect
  return number;
}

printNum(2)

함수는 인풋을 받아서 아웃풋을 내면 끝인데 그런데 그거말고 다른 행위(console.log)를 하고 있다.

그러므로 이것도 엄밀히 말하면 Side Effect다.

 

 

예제2

const num = 3;

function printNum(number){
  const result = number + num; //Side Effect
  return result;
}

printNum(2);

Output을 내는 과정에서 필요한게 아닌가 생각할 수 있지만 함수가 Input으로 받은 것들 외에 외부에 있는 나머지 값들(num)을 읽어오는 행위를 했다.

이러한 행위도 Side Effect다.

 

 

예제3

function printNum(number){
  const result = number + 2 // NOT Side Effect!!
  return result;
}

printNum(2);

이건 외부의 값을 받아오지 않았기에  Side Effect가 아니다. 

외부의 값을 가져와서 읽거나 변화시키거나 어떤 행위를 하면 SideEffect.

 

내가 인자로 받은 것들을 함수 안에서 필요한 것들로만 조합해서 Output되면 그냥 함수의 계산 과정인 것이다.

 

2. useEffect

더보기
더보기
더보기

2. Effect Hook


<첫번째 Rendering Block 예시>

const num = 3;

function printNum(number){
  const result = number + num; // sideEffect
  //
  1분걸림
  //
  
  
  return result; // UI rendering
}

printNum(2);

 

2-1  Side Effect를 그냥 함수에서 쓰던 것처럼 일으키면 안된다.

 

첫번째.  UI 랜더링 Block 문제 (Rendering Block)

 

UI를 렌더링하려면 함수형 컴포넌트에서 리턴(JSX)을 해줘야 한다.

그런데 Side Effect는 리턴을 하기 전에 발생하는 행위들이다.

만약,  return까지 도달하는데 1분이 걸린다면 그동안 UI 랜더링이 안일어난다. 그러면 유저들은 그 시간동안 기다리지 않고 사용하지 않을 것이다.

 

 

두번째.  트리거(Trigger)

 

Rerender(리랜더)는 화면을 다시 그리는 것이다. 화면을 다시 그릴 때는 함수형 컴포넌트를 다시 호출한다.

 

함수 본문에서 Data fetching을 하면 state가 바뀌면 다시 데이터를 fetching한다. 

Data fetching은 시작할 때 한 번만 필요하다. 나머지 때는 계속 기억만 하고 있으면 된다. 

계속 요청하면 서버에 불필요한 부하가 일어나고 클라이언트도 필요없는 연산을 계속 하게 된다.

즉, 특정 시점에만 실행을 하고 싶은데 함수 본문에 넣으면 매번 실행되는 트리거 문제가 있다.

 

 

이런 문제를 하는게 useEffect!

 

 

2-2 useEffect

import React, { useState, useEffect } from "react";

export function Main(){
  const [count, setCount] = useState(0);
  const [toggle, setToggle] = useState(false);
  
  useEffect(()=>{
   console.log("effect trigger");
  }, []);
  
  console.log("function exe");
  
  return(
    <>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>up</button>
      <div>
        <h1>{toggle ? "on" : "off"}</h1>
        <button onClick={() => setToggle(!toggle)}>toggle button</button>
      </div>
    </>
  );
}

export function SecondPage(){
  return <h1>Hello, JaseSang</h1>
}

// console.log 실행순서 
// function exe => effect trigger

useEffect(effect, dependency array)

  • 첫번째 인자는 우리가 발생시키기 원하는 effect를 넣는 것으로 함수가 들어간다. 이 함수는 렌더링이 끝나고나면 실행된다.  => 첫번째 UI 랜더링 Block 문제 (Rendering Block) 해결

 

  • 두번째 인자는 의존성 배열로 얘네가 바뀔 때만 실행하라는 의미이다. => 두번째 Trigger 해결 
    • 두번째 인자를 넣지 않으면 매번 렌더링 할때마다 실행한다.
    • [count]만 넣으면 카운트가 바뀔 때마다, [toggle]만 넣으면 토글이 바뀔 때마다 실행된다.
    • []빈 배열을 넣으면 처음에만 렌더된다. (data fetching을 여기서 하면 된다)

 

  • 이러한 useEffect를 이용하면 stater값이 잘 변하는지 디버깅 할 때도 쓰일 수 있다.

 

 //모든 랜더링마다
 
 useEffect(()=>{
   console.log("effect trigger");
  });
  
 //count마다
 
 useEffect(()=>{
   console.log("count changed", count);
  },[count]);
  
 //toggle마다
  
 useEffect(()=>{
   console.log("toggle changed", toggle);
  }, [toggle]);
  
  //첫번째 랜더링만
  
 useEffect(()=>{
   console.log("empty dependency", count);
  }, []);

 

 

Mount

첫번째 랜더링 때는 모든 effect가 다 실행된다.
'첫번째 랜더링이 됐다는 것은 이 컴포넌트가 비로소 화면에 들어왔다.'라는 의미다. 
그래서 첫번째 랜더링을 보통 Mount(마운트)라고 부른다. 
화면에 마운트가 됐다(화면에 처음 들어왔다.)

 

 

관심사의 분리

effect는 내가 하고자하는 동작별로 분리되어 있는게 가장 좋다.
하나의 useEffect에 다 쓰면 유지보수 측면에서 불리하다. 여러개가 들어가 있으면 나중에 뭐가 뭔지 헷갈리게 된다.

ex) Data fetching의 경우
한 번에 useEffect(() => {} , [])안에 다 넣을 수 있지만 만약 프로필, 이미지, 댓글 등등 여러개의 fetch를 사용하면 나중에 어떤 것이 프로필이고 댓글인지 헷갈리게 된다. 그래서 여러개의 useEffect로 분리해야 하는 것이다.

 

 

 

 

3. Clean up Effect

더보기
더보기
더보기

 

3. Clean up Effect


이전에 내가 발생시킨 effect가 있다. 그 effect가 단발성(ex> console.log)이면 괜찮다.

그런데 어떤 이벤트를 구독을 붙이면 그 구독을 해제하지 않는다면 그 이벤트는 끝날 때까지 붙어있다. (사용자가 탭을 닫을 때까지)

 

 

import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";

export function Main(){
  const [count, setCount] = useState(0);
  
  useEffect(()=>{
   setInterval(()=>{
     console.log("timer");
   },1000)
  }, []);
  
  console.log("function exe");
  
  return(
    <>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>up</button>
      <div>
        <Link to="/second">go to second</Link>
      </div>
    </>
  );
}

export function SecondPage(){
  return <h1>Hello, JaseSang</h1>
}
첫번째 페이지                                                                                                   두번째 페이지

 

첫 페이지에서 타이머를 effect로 부르고 두번째 페이지로 가면 구독을 하고 지우지 않았기 때문에 페이지가 바뀌었음에도 타이머는 계속 올라가고 있다.

 

SPA이기 때문에 하나의 html에서 계속 돈다. (페이지를 싹다 치우고 다시 받아오는게 아니다.)

그러면 이렇게 불필요한게 계속 돌고 있는 것이다.

 

만약 여기다가 백엔드로부터 api를 호출했다면 1초마다 api를 호출하는 꼴이 된다. 사용자가 많아지면 서버는 터질 가능성이 높아진다. 그래서 이걸 지워야 하는 것이다. 

 

이걸 클린업(Clean up)이라고 한다.

 

 

<첫번째 예시>

  useEffect(()=>{
   setInterval(()=>{
     console.log("timer");
   },1000)
   
   return () => {
    console.log("cleanup effect");
    clearInterval(id);
   }
  }, []);

 

  • effect 함수 안에 return으로 어떤 함수를 리턴해주면 된다.
  • 언제 발생되냐면
    1.  마운트된 시점에 useEffect가 발생한다.
    2.  의존성 배열이 []이므로 다시 effect가 발생하지 않는다.
    3.  언마운트(화면에서 사라졌다) 컴포넌트가 화면에서 사라지면 실행되는게 return 부분이다.

 

 

 

사실 클린업 effect는 언마운트의 경우에만 발생하는게 아니다.

첫번째 페이지에서 up버튼을 누르면 사실상 클린업 이펙트가 발생하는 정확한 시점은 엄밀히 따지자면 다음 effect가 발생하기 전에 실행하는 것이다.

 

<두번째 예시>

useEffect(() => {
  console.log("count efft")
  console.log("count", count);
  
  return () => {
    console.log("count clean up");
  };
}, [count]);

//console.log 순서

//count efft
//count 0
//count clean up   <= up버튼 클릭
//count efft
//count 1

 

세탁기에 빨래를 넣었다. 다음 빨래를 돌리기 위해서는 세탁기에 이미 들어가 있는 완료된 세탁물을 빼낸 다음에 넣는다. 

우리가 일상생활에서 하는 클린업처럼 그것과 똑같이 새로운 effect가 발생하기 전에는 항상 클린업을 먼저 돌려준다.

 

  • 첫 랜더링 때는 클린업 할게 없으니 클린없이 없다. 그 다음 이펙트가 발생할 때는 계속해서 클린업이 트리거 된다.
  • 우리가 뭔가 기존의 것을 취소하거나 무언가를 할 때 이럴 때 쓰면된다. 만약에 컴포넌트가 언마운트 되면 모든 클린업이 한 번 쭉 실행된다.
  • 첫번째 예시는 다음 effect가 없기 때문에 언마운트 될 때 그 시점에서 한 번만 클린업이 실행되었다.
  • 두번째 예시는 다음 effect가 실행되기 전에 무조건 클린업이 돌아가는 것이다.

 

 

 

 

 

'React > Today I learned' 카테고리의 다른 글

[React] hook, props, state, event + batch  (0) 2021.12.10
리액트 매우 쉬운 별점기능 구현  (0) 2021.07.11
fetch함수를 이용한 로그인&회원가입  (0) 2021.07.02
state,props,event  (0) 2021.07.01
Sass  (0) 2021.06.30

+ Recent posts