hook flow

박상욱·2022년 3월 2일
0

React

목록 보기
10/20

hook flow - component를 그리고 hook을 사용하였는데 그 hook이 언제 불리고 언제 사라지고 중첩적으로 comp 만들었을때는 comp간 hook의 호출 타이밍은 언제인지?

hook의 호출 타이밍

ex) 일반적인 순서 알아보기

 <script type="text/babel">
      const rootElement = document.getElementById("root");

      const App = () => {
        console.log("APP render start");

        const [show, setShow] = React.useState(() => {
          //lazy initialize
          console.log("APP useState call");
          return false;
        });

        React.useEffect(() => {
          console.log("APP useEffect, no deps");
        });

        React.useEffect(() => {
          console.log("APP useEffect, empty deps");
        }, []);

        React.useEffect(() => {
          console.log("APP useEffect, [show] deps");
        }, [show]);

        function handleClick() {
          //set함수는 인자로 이전값이 들어온다.
          //setState의 처번째 함수는 이전값이다.
          setShow((prev) => !prev);
        }

        const InputBox = () => {
          return (
            <>
              {" "}
              <input />
              <p></p>
            </>
          );
        };

      console.log("APP render end");
      
        return (
          <>
            <button onClick={handleClick}>search</button>
            {show && <InputBox />}
          </>
        );
      };
      ReactDOM.render(<App />, rootElement);
    </script>

호출 순서

APP render start -> useState -> APP render end -> useEffect(선언 순서로 호출됨)
-> button click ->
APP render start -> APP render end -> useEffect([] 제외)

중요한것 ! render가 끝난다음에 useEffect가 동작한다.

이유는 ??? 사이드 이펙트이기 때문에 일단 다 그려지고 난 뒤 이펙트들에 의한 동작들에 대한 이펙트를 발생시킨다.

code tip

setState 함수는 첫번째 인자로 이전 state의 값을 가지고 있다.

ex) Child component가 있을 경우 순서 알아보기

    <script type="text/babel">
      const rootElement = document.getElementById("root");

      const Child = () => {
        console.log("Child render start");
        const element = (
          <>
            {" "}
            <input />
            <p></p>
          </>
        );
        console.log("Child render end");
        return element;
      };

      const App = () => {
        console.log("APP render start");

        const [show, setShow] = React.useState(() => {
          //lazy initialize
          console.log("APP useState call");
          return false;
        });

        React.useEffect(() => {
          console.log("APP useEffect, no deps");
        });

        React.useEffect(() => {
          console.log("APP useEffect, empty deps");
        }, []);

        React.useEffect(() => {
          console.log("APP useEffect, [show] deps");
        }, [show]);

        function handleClick() {
          //set함수는 인자로 이전값이 들어온다.
          //setState의 처번째 함수는 이전값이다.
          setShow((prev) => !prev);
        }

        console.log("APP render end");

        return (
          <>
            <button onClick={handleClick}>search</button>
            {show ? <Child /> : null}
          </>
        );
      };
      ReactDOM.render(<App />, rootElement);
    </script>


호출 순서

APP render start -> useState -> APP render end -> useEffect(선언 순서로 호출됨)
-> button click ->
APP render start -> APP render end ->
-> Child render start -> Child render end -> useEffect([] 제외)

ex) Child 안에 useState와 event가 있을 경우 순서 알아보기

    <script type="text/babel">
      const rootElement = document.getElementById("root");

      const Child = () => {
        console.log("   Child render start");

        const [text, setText] = React.useState(() => {
          console.log("   Child useState");
          return "";
        });

        React.useEffect(() => {
          console.log("   Child useEffect, no deps");
        });

        React.useEffect(() => {
          console.log("   Child useEffect, empty deps");
        }, []);

        React.useEffect(() => {
          console.log("   Child useEffect, [text] deps");
        }, [text]);

        const handleChange = (e) => {
          setText(e.target.value);
        };

        const element = (
          <>
            {" "}
            <input onChange={handleChange} />
            <p>{text}</p>
          </>
        );
        console.log("   Child render end");
        return element;
      };

      const App = () => {
        console.log("APP render start");

        const [show, setShow] = React.useState(() => {
          //lazy initialize
          console.log("APP useState call");
          return false;
        });

        React.useEffect(() => {
          console.log("APP useEffect, no deps");
        });

        React.useEffect(() => {
          console.log("APP useEffect, empty deps");
        }, []);

        React.useEffect(() => {
          console.log("APP useEffect, [show] deps");
        }, [show]);

        function handleClick() {
          //set함수는 인자로 이전값이 들어온다.
          //setState의 처번째 함수는 이전값이다.
          setShow((prev) => !prev);
        }

        console.log("APP render end");

        return (
          <>
            <button onClick={handleClick}>search</button>
            {show ? <Child /> : null}
          </>
        );
      };
      ReactDOM.render(<App />, rootElement);
    </script>

호출 순서

부모가 다 그려지고 -> 자식 clidren 다 그려짐 -> 자식 side effect -> 부모 side effect -> button click -> 부모 그려지고 -> 부모 side effect

useEffect의 clean up을 이용하여 세밀한 log보기

CleanUp

useEffect의 함수안에서 return 하면서 어떤 함수를 호출하게 되면
이 함수의 side effect가 setup되어 있는 것을 지우고 다시 생성한다.

<script type="text/babel">
      const rootElement = document.getElementById("root");

      const Child = () => {
        console.log("   Child render start");

        const [text, setText] = React.useState(() => {
          console.log("   Child useState");
          return "";
        });

        React.useEffect(() => {
          console.log("   Child useEffect, no deps");

          return () => {
            console.log("   Child useEffect,[Clean up] no deps");
          };
        });

        React.useEffect(() => {
          console.log("   Child useEffect, empty deps");

          return () => {
            console.log("   Child useEffect,[Clean up] empty deps");
          };
        }, []);

        React.useEffect(() => {
          console.log("   Child useEffect, [text] deps");

          return () => {
            console.log("   Child useEffect,[Clean up] [text] deps");
          };
        }, [text]);

        const handleChange = (e) => {
          setText(e.target.value);
        };

        const element = (
          <>
            {" "}
            <input onChange={handleChange} />
            <p>{text}</p>
          </>
        );
        console.log("   Child render end");
        return element;
      };

      const App = () => {
        console.log("APP render start");

        const [show, setShow] = React.useState(() => {
          //lazy initialize
          console.log("APP useState call");
          return false;
        });

        React.useEffect(() => {
          console.log("APP useEffect, no deps");

          return () => {
            console.log("APP useEffect,[Clean up] no deps");
          };
        });

        React.useEffect(() => {
          console.log("APP useEffect, empty deps");

          return () => {
            console.log("APP useEffect,[Clean up] empty deps");
          };
        }, []);

        React.useEffect(() => {
          console.log("APP useEffect, [show] deps");

          return () => {
            console.log("APP useEffect,[Clean up] [show] deps");
          };
        }, [show]);

        function handleClick() {
          //set함수는 인자로 이전값이 들어온다.
          //setState의 처번째 함수는 이전값이다.
          setShow((prev) => !prev);
        }

        console.log("APP render end");

        return (
          <>
            <button onClick={handleClick}>search</button>
            {show ? <Child /> : null}
          </>
        );
      };
      ReactDOM.render(<App />, rootElement);
    </script>

호출 순서

부모가 다 그려지고 -> 자식 다 그려짐 -> 부모 side effect CleanUp -> 자식 side effect start-> 부모 side effect start -> text 입력 -> 자식 다 그려짐 -> 자식 side effect CleanUp -> 자식 side effect start -> button click -> 부모 그려지고 -> 자식 side effect CleanUp -> 부모 side effect CleanUp -> 부모 side effect start

useEffect는 어떤 변경에 따라서 실행이 되는데 실행이 되기전에 CleanUp이 먼저 일어나는 것이다.

Child component가 unMount(사라질 때) CleanUp은 다이루어 진다.

  • 즉 동작을 useEffect로 다 등록해놓았을때 이 component가 종료되면 어떻게 되지 라고 고민할 필요가 없다. react가 알아서 cleanUp을 해준다.
  • 최초 useEffect가 실행되었을때는 cleanUp이 일어나지 않고 최초 한번이라도 실행되었을 경우 cleanUp이 실행된다.
    ex) component가 unMount 될때 localStorage를 지워라.

정리

  • useEffect -> render가 끝난뒤 호출
  • update시 -> useEffect clean up 먼저, useEffect
    • ( 처음 생성시에는 cleanUp 먼저 호출 안함 useEffect만 호출 한 번이라도 useEffect가 등록 되어 있으면 cleanUp하고 useEffect에 update된 내용을 반영한다. 불변의 상황을 계속 만들려고하는 것 -> 있던 것을 지우고 새로운것을 다시 갈아끼우는)
  • dependency array -> 전달받은 값의 변화 있는 경우에만
profile
개발자

0개의 댓글