본문 바로가기
WEB/기초

[WEB] React, useEffect 짧게.

by IT황구 2021. 7. 30.
728x90
반응형

 

반갑습니다.

빠르게 하겠습니다.

 

Hook중에 useEffect가 있습니다.

class형 컴포넌트의 라이프사이클에서 3가지를 합친것입니다.

 

componentDidUpdate, componentDidMount, componentWillUnmount를 합친것입니다.

 

이걸 한개의 Hook에서 사용한다고?

오우 개꿀!

하지만 사용방법을 알아야겠죠?

네~ 알아보겠습니다

(요약만 보실분은 맨 아래만 보시면 됩니다)

 

기본 모양

useEffect(effect, deps); 이런 형태를 가지고 있습니다.

 

useEffect는 side effect를 하게 해주는 훅 이라고 생각하시면 좋습니다.

side effect는 여러가지가 될 수 있습니다. 값 올려주기, 비동기처리 함수 불러주기 등등.. 여러가지가 있을 수 있습니다.

 

그리고 useEffect는 반드시 Component가 모두 렌더링 되고 화면에 다 그려진 '후'에 호출이 됩니다.

이것의 장점은 컴포넌트가 중간에 useEffect에 의해서 영향을 받아 모양이 바뀌는것을 방지해줍니다.

 

물론 Component가 만들어지는 동안에 interact 해서 모양이 바뀌어야 하는 부분이 있을수도 있습니다. 이런것은

useLayoutEffect라는 Hook에서 처리할 수 있습니다. useEffect와 똑같은 기능을 하지만, 호출이 되는 시점이 다릅니다.

 

 

이걸 호출하면 어떻게 될까요?

맨 처음에 render이 호출되고, 화면이 다 그려졌으면 useEffect가 호출됩니다.

 

마치 componentDidMount처럼 일어납니다.

 

자 그러면,

 

function App() {
  const [textA, setTextA] = useState("");
  const [textB, setTextB] = useState("");
  useEffect(() => {
    console.log("useEffectCalled");
  });
  return (
    <div>
      <div>{textA}</div>
      <div>{textB}</div>
      <button onClick={() => setTextA((prev) => prev + "TextA")}>
        SetTextA
      </button>
      <button onClick={() => setTextB((prev) => prev + "TextB")}>
        SetTextB
      </button>
    </div>
  );
}

export default App;

 

이렇게 버튼을 만들고, 버튼을 클릭할때마다 무슨일이 일어나는지 봅시다.

 

보시다시피, button을 클릭할때마다 useEffect가 call이 됩니다. 저걸 멈추게 하려면 어떻게 해야할까요?

 

useEffect의 2번쨰 param인 deps(dependency)를 처리해주면 됩니다.

 

useEffect(() => {

console.log("useEffectCalled");

}, []);

이렇게 코드를 수정해봅시다.

depend를 하는 변수를 []안에 넣어주면, []안의 변수가 바뀔때만 useEffect가 호출이 됩니다.

이렇게 빈칸으로 주면 영향을 받는애들이 없기떄문에, componentDidMount처럼 한번만 맨 처음에 호출되고 더이상 불리지 않습니다.

//

This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run.

docs 내용인데, 제가 선언한 effect가 어떤 값하고도 의존관계가 없으니까 절대 re-run이 일어나지 않는다는 말 입니다.

//

사실 []내부는 componentDidUpdate처럼

prev value === current value 를 비교해서 다를때만 update를 한답니다.

 

이제 실행해보면

 

이렇게 render만 일어나고, useEffect는 일어나지 않는걸 볼 수 있습니다.

 

 

그러면 deps에 textB를 넣어보겠습니다.

그렇게 하면 textB가 수정될때만 useEffect가 발생합니다.

textA를 바꾸는 setTextA버튼은 아무리 눌러도 아무일도 없을것입니다. 왜냐하면 dependency에서 관리하지 않거든요.

 

보시면 알겠지만, render만 계속 일어나고 있습니다.

 

그렇다면 textB를 누르면 어떻게 될까요?

값이 분명 바뀌지요?

prev => ""

current => "textB"

 

순서가 어떻게 된 것일까요?

setTextB가 눌리면서 render가 한번 일어납니다.

그리고나서 useEffect 도 일어나고, 그 위에있는 setTextA("CC") 때문에 또 한번의 render가 일어납니다

총 2번의 렌더링이 일어나게 됩니다.

 

또한

docs를 읽다보면 clean-up 이라는 표현을 보게 될 것입니다.

useEffect에서는 cleanup이 일어난다고 합니다.

이게 왜 필요할까요?

 

참고로 clean-up은 return () =>{ } 에서 일어납니다.

 

이건 페이스북 예시입니다.

처음에 props.source.subscribe()의 return이 true라고 가정합시다.

 

그러면 subscription = true 입니다.

props.source가 수정되어, useEffect가 또 불립니다.

subscription이 true인데 또 구독을 할 수 있을까요?

이러면 안됩니다.

 

수정이 되기 전에 subscription = false로 만들어야 합니다.

이걸 바로 clean-up에서 해줄 수 있습니다.

 

clean-up 함수는, 새로운 업데이트가 일어나기 바로 전에 일어납니다.

무슨 말인지 잘 와닿지 않으시죠?

아까 했던 예제로 한번 보여드리겠습니다.

 

import "./App.css";
import { useDispatch, useSelector } from "react-redux";
import { actionCreator } from "./reducer/configureStore";
import { useEffect, useState } from "react";

function App() {
  const [textA, setTextA] = useState("");
  const [textB, setTextB] = useState("");
  useEffect(() => {
    console.log("useEffectCalled");
    return () => {
      console.log("cleanup에 있는 before textB = " + textB);
    };
  }, [textB]);
  console.log("render");
  return (
    <div>
      <div>{textA}</div>
      <div>{textB}</div>
      <button onClick={() => setTextA((prev) => prev + "TextA")}>
        SetTextA
      </button>
      <button onClick={() => setTextB((prev) => prev + "TextB")}>
        SetTextB
      </button>
    </div>
  );
}

export default App;

 

 

 

보시면 딱 한번 눌렀습니다.

한번 눌렀으니, render가 가장 먼저 일어납니다.

그 이후에 useEffect가 불리는데, clean-up이 있네요? 그걸 먼저 실행합니다.

 

사실 render를 먼저 했으면 textB에는 ="textB"가 들어가 있어야겠죠?

하지만 prevState를 가지고 있는 cleanup function 안에서는 textB에는 이전 값인 ""가 들어가있습니다.

그 이후에 useEffect가 불리게 됩니다.

 

한번 더 클릭하면 어떻게 될까요? (참고로 deps에 textB가 있기에 해당이 되어서 cleanup 또한 일어나는 것입니다)

 

예상하신대로 나왔나요?

render가 되어서, 화면에는 TextBTextB로 찍히고

cleanup에서는 과거의 값인 textB = "TextB" 로 찍힌 후에

useEffect가 불린답니다..

 

만약 deps 부분을 [textA] 로 고친다면 render만 일어날 것입니다.

 


 

결론 요약

1. deps를 아무것도 안 줄 경우
-> render마다 side effect, clean-up 모두 불린다.

useEffect ( () => {
   console.log("hi!");
});

2. deps를 []로 줄 경우
-> render시에 딱 한번 componentDidMount처럼 불린다. 그 이후에는 side effect, clean-up 모두 불리지 않는다.
 This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run. 
useEffect ( () => {
   console.log("hi!");
}, []);

3. deps에 특정 변수를 줄 경우
-> (cntA가 변할때만) render하고 clean-up function이 불리고, useEffect 내부가 호출된다. 
useEffect(() => {
    console.log("useEffect First!");
    console.log(`inner stateA = ${cntA}`);
    console.log(`inner stateA = ${cntB}`);
    return () => {
      console.log("clean up function");
      console.log(`outer stateA = ${cntA}`);
      console.log(`outer stateA = ${cntB}`);
    };
  }, [cntA]);

 

 

redux 포스팅하다가 제가 몰라서 정리한거랍니다 ㅎ

캐싱을 위해서는

useMemo, useCallback을 사용하자!

 

 

728x90
반응형