react 함수형 컴포넌트의 실행 순서가 어떻게 되나요?

2019. 02. 15. 15:07

react 에서 hook에 대해서 알아보고 있는데,

샘플 예제의 실행순서가 너무나도 헷갈립니다.

아래에 샘플을 실행시켜 보면 결과는 가장 아래에 있는 콘솔로그 캡쳐화면 처럼 나옵니다.

결과를 보면,

질문1. console.log('1')과, console.log('2')가 4번이나 찍혀 있습니다.

그렇다는건 useRequest.js와 Post.js가 4번 실행이 됐다는 것인데, 어떻게 4번이 실행이 되는지 궁금합니다.

질문2. 실행순서를 보시면,

2-1 다음에 1 이 실행

2-2 다음에 1 이 실행

2-3 다음에 1 이 실행

2-4에서 끝

이렇게 되는데, 왜 이런식으로 실행 순서가 되는지도 궁금합니다.

질문3. 마지막 실행이 왜 1-3 이 아니고 2-4 인것도 궁금합니다.

/* useReqeust.js */
import React, {useEffect, useState} from 'react';
import axios from 'axios';

const useRequest = (url) => {
    const [loading, setLoading] = useState(false);
    const [response, setResponse] = useState(null);
    const [error, setError] = useState(null);
    console.log('2');
    useEffect(
        async () => {
            console.log('2-0');
            setError(false);

            try {
                setLoading(true);
                console.log('2-1');
                const res = await axios.get(url);
                console.log('2-2');
                setResponse(res);
                console.log('2-3');
            } catch (e) {
                setError(e);
            }

            setLoading(false);
            console.log('2-4');
        },
        [url]
    );
    console.log('2-5');
    return [response, loading, error];
}

export default useRequest;

/* Post.js */
import React from 'react';
import useRequest from './hooks/useRequest';

const Post = () => {
    console.log('1');
    const [response, loading, error] = useRequest('https://jsonplaceholder.typicode.com/posts/1');
    console.log('1-1');
    
    if (loading) {
        return <div>loading...</div>;
    }

    if (error) {
        return <div>Error!</div>;
    }

    if (!response) {
        console.log('1-2');
        return null;
    }

    const {title, body} = response.data;
    console.log('1-3');
    return (
        <div>
            <h1>{title}</h1>
            <p>{body}</p>
        </div>
    );
};

export default Post;

/* App.js */
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
// import Form from './Form';
// import Counter from './Counter';
import Post from './Post';

class App extends Component {
  render() {
    return (
      <div className="App">
        <Post />
      </div>
    );
  }
}

export default App;

공유하고 돈벌기 ♥︎

총 1개의 답변이 있습니다.

질문자 채택 답변
OpenSG 수석 컨설턴트

단순하게 생각하면 상태를 3번 변경했기 때문입니다. useRequest.js 내부의 try { } 내부에서 상태를 변경하는 작업이 3번 일어났지요?

setLoading(true) , setResponse(res), 마지막에 setLoading(false) 까지요. 여기에 더해서 화면을 초기화할 때까지 총 4번입니다. React Functional Component는 함수 자체가 render() 함수라고 생각하셔도 됩니다. 컴포넌트는 초기화 될때 상태나 속성이 바뀔때 함수를 호출하여 렌더링한다는 점을 생각하시면 간단합니다.

  1. 첫번째 화면이 초기화되고 나서 component가 마운트되면서 2-1, 1을 실행하구요.

  2. setLoading(true)로 인해서 loading 상태가 변경되면 Component는 상태가 변경되었을 때의 흐름을 거칩니다. 그러면서 다시 함수가 호출되면서 Redering이 일어납니다.

  3. 그후 axios로 비동기 요청하구요. 그동안에 loading 속성이 true이니 화면에는 loading... 이 렌더링되어 있겠군요. 그후 axios 요청의 결과로 서버로부터 응답이 와서 데이터를 수신하게 되면 2-2가 찍힙니다.

  4. setResponse(res)가 호출되면 다시 상태가 바뀌니 Post 컴포넌트가 렌더링되면서 1이 콘솔에 출력됩니다. 그후 2-3찍힘..

  5. 그후 다시 setLoading(false)가 호출되며 상태가 바뀌었으니 다시 렌더링...그 결과 1이 찍힙니다.

  6. 마지막으로 2-4가 찍히고 끝나겠네요.

결론적으로 말씀드리고 싶은 것은 React Hook을 제대로 이해하시려면 React Component의 Life cycle 을 정확하게 알고 계셔야 한다는 겁니다. React V16.4 이후의 생명주기(life cycle)을 다시 한번 꼼꼼히 학습해보세요.

님께서 질문하신 코드를 codesandbox에서 다시 한번 실행하면서 확인해보았습니다. 사용하신 API가 지연시간이 발생하지 않아서 'loading...' 메시지를 볼수가 없었습니다. 제가 만들어놓은 1초의 지연시간을 발생시키는 API를 사용하도록 코드를 수정해보았습니다. 이제 loading... 메시지를 볼수가 있을 겁니다. 아래 경로에 들어가보세요.

https://codesandbox.io/s/y2y0r4v8vj

2019. 02. 16. 14:30
73