개발조각

[React] useEffect 본문

React

[React] useEffect

개발조각 2023. 9. 12. 21:04
728x90
반응형

리액트 훅 중에서 가장 중요한 게 무엇인지 물어본다면 useEffect라고 대답할 것이다.

 

useEffect란? (개념설명)


어떠한 컴포넌트가

  • Mount (화면에 첫 렌더링)
  • Update (다시 렌더링)
  • Unmount (화면에서 사라질 때)

특정 작업을 처리할 코드를 실행시키고 싶을 때 useEffect를 사용한다.

 

useEffect는 콜백함수를 받는데

(콜백함수란 다른 함수의 인자로 전달된 함수를 의미한다.)

콜백함수 내부에 원하는 작업을 전달해 줄 코드를 작성하면 된다.

useEffect(()=>{작업...})

 

useEffect는 두 가지 형태를 알고 있으면 된다.

1. 하나의 인자만 사용(콜백함수)

useEffect(()=>{
	// 작업...
});
  • 컴포넌트가 렌더링 될 때마다 실행

 

2. 두 개의 인자 사용(콜백함수, deps Array)

useEffect(()=>{
	// 작업...
}, [value]);
  • 화면에 첫 렌더링 될 때 실행
  • value값이 바뀔 때 실행
useEffect(()=>{
	// 작업...
}, [])
  • 화면에 첫 렌더링 될 때 실행

 

Clean Up - 정리

useEffect안에 기능을 등록했으면 정리작업을 해줘야 된다.

(구독, 타이머, 이벤트리스너 등 등록했으면 정리작업을 해줘야 된다.)

useEffect(()=>{
	// 구독...
    return ()=>{
    	// 구독 해지...
    }
}, []);

정리 작업을 처리해 주려면 useEffect의 return값으로 함수를 넣어주면 되고,

이 함수 안에서 원하는 정리하는 작업을 해주면 된다.

함수를 리턴해주면 해당 컴포넌트가 언마운트 될 때 혹은 다음 렌더링 될 시 불리는 useEffect 실행되기 이전에 함수가 실행이 된다.

 

예제 1 (카운트, 인풋)


useEffect에 콜백함수만 있을 경우

import { useState, useEffect } from "react";

function App() {
    return (
        <>
            <UseEffect1 />
        </>
    );
}

const UseEffect1 = () => {
    const [count, setCount] = useState(1);

    const handleCountUpdate = () => {
        setCount(count + 1);
    };

    // 렌더링 될때마다 매번 실행됨
    UseEffect1(()=>{
      console.log("렌더링 🎨")
    })

    return (
        <div>
            <button onClick={handleCountUpdate}>Update</button>
            <span>count: {count}</span>
        </div>
    );
};

export default App;

마운트 될때 콘솔 찍힘

마운트 될 때(화면 첫 렌더링) useEffect가 실행된다.

state가 업데이트 될때마다 렌더링 찍힘

state가 업데이트될 때마다 화면이 렌더링 되므로  useEffect가 계속 실행된다.

자세히 얘기하자면 useEffect의 콜백은 컴포넌트가 화면 렌더링된 직후에 불리는 것이다.

 

name useState를 추가할 경우

import { useState, useEffect } from "react";

function App() {
    return (
        <>
            <UseEffect1 />
        </>
    );
}

const UseEffect1 = () => {
    const [count, setCount] = useState(1);
    const [name, setName] = useState("");

    const handleCountUpdate = () => {
        setCount(count + 1);
    };
    const handleInputChange = ({ target }) => {
        setName(target.value);
    };

    // 렌더링 될때마다 매번 실행됨
    useEffect(() => {
        console.log("렌더링 🎨");
    });

    return (
        <div>
            <button onClick={handleCountUpdate}>Update</button>
            <span>count: {count}</span>

            <input type="text" value={name} onChange={handleInputChange} />
            <span>name: {name}</span>
        </div>
    );
};

export default App;

이렇게 되면 입력할 때마다 useEffect가 계속 실행이 되고,

useEffect안에 조금이라도 무거운 작업을 한다면 굉장히 비효율적이게 된다.

 

state가 렌더링 될 때마다 useEffect를 실행시키는 것이 아닌

count state가 업데이트될 때마다 useEffect를 실행시키고 싶다면 두 번째 인자에 배열을 주고 배열 안에다가 count를 넣어주면 된다.

여기서 두 번째 인자의 배열을 다른 말로 dependency array라고 한다.

// 마운트 + [item]변경될때만 실행.
useEffect(() => {
    console.log("count 변화 🧨");
}, [count]);

useEffect는 컴포넌트가 화면에 렌더링 되었을 때, count값이 변화될 때만 불리게 되고,

인풋에 입력해도 useEffect가 작동하지 않는다.

 

name값이 변화될 때만 불리게 하고 싶을 경우

// 마운트 + [item]변경될때만 실행.
useEffect(() => {
    console.log("name 변화 ⭐️");
}, [name]);

업데이트를 클릭해도 useEffect가 작동하지 않는다.

 

여러 가지 useEffect를 실행할 경우

import { useState, useEffect } from "react";

function App() {
    return (
        <>
            <UseEffect1 />
        </>
    );
}

const UseEffect1 = () => {
    const [count, setCount] = useState(1);
    const [name, setName] = useState("");

    const handleCountUpdate = () => {
        setCount(count + 1);
    };
    const handleInputChange = ({ target }) => {
        setName(target.value);
    };

    // 렌더링 될때마다 매번 실행됨
    useEffect(() => {
        console.log("렌더링 🎨");
    });

    // 마운트 + [item]변경될때만 실행.
    useEffect(() => {
        console.log("count 변화 🧨");
    }, [count]);

    // 마운트 + [item]변경될때만 실행.
    useEffect(() => {
        console.log("name 변화 ⭐️");
    }, [name]);

    return (
        <div>
            <button onClick={handleCountUpdate}>Update</button>
            <span>count: {count}</span>

            <input type="text" value={name} onChange={handleInputChange} />
            <span>name: {name}</span>
        </div>
    );
};

export default App;

화면 첫 렌더링 시에는 3개의 useEffect가 출력되지만

count state 업데이트 시에는 콜백함수만 있는 useEffect, dependency array에 count가 있는 useEffect만 실행된다.

그리고 name state 업데이트 시에는 콜백함수만 있는 useEffect, dependency array에 name이 있는 useEffect만 실행된다.

 

useEffect를 맨 처음 화면 렌더링 될 때만, 마운팅될때만 실행시키고 싶다면  dependency array에 빈배열을 넣어주면 된다.

useEffect(() => {
    console.log("마운팅 👔");
}, []);

 

예제 2 (Clean up)


const Timer = (props) => {
    useEffect(() => {
        const timer = setInterval(() => {
            console.log("타이머 돌아가는중...");
        }, 1000);
    }, []);

    return (
        <div>
            <span>타이머를 시작합니다. 콘솔을 보세요!</span>
        </div>
    );
};

맨 처음 브라우저에 마운팅 되었을 때 useEffect안에 있는 콜백함수가 실행이 된다.  

 

const UseEffect2 = () => {
    const [showTimer, setShowTimer] = useState(false);
    return (
        <div>
            {showTimer && <Timer />}
            <button onClick={() => setShowTimer(!showTimer)}>
                Toggle Timer
            </button>
        </div>
    );
};

Toggle Timer를 클릭하면 타이머가 실행이 되는데, Toggle Timer를 다시 클릭해도 타이머가 멈추지 않는다.

이유는 setInterval로 타이머를 만들었는데 만든 타이머를 정리해주지 않았기 때문이다.

(여기서 정리란 타이머를 다 쓰고 나서 종료해 주는 것을 말한다.)

 

정리를 해주기 위해서는 useEffect의 return값으로 함수를 넣어주면 된다.

그 함수 안에서 정리 작업을 처리해 주면 된다.

useEffect(() => {
    const timer = setInterval(() => {
        console.log("타이머 돌아가는중...");
    }, 1000);

    return () => {
        clearInterval(timer);
        console.log("타이머가 종료되었습니다.");
    };
}, []);

useEffect는 두 번째 인자로 빈배열을 받기 때문에 timer변수 부분이 맨 처음에 렌더링 되었을 때 그때만 돌리게 된다.

리턴 뒤에 붙은 함수는 정리해 주는 함수이고 이 함수는 타이머 컴포넌트가 언마운트 될 때(화면에서 사라질 때)만 실행이 된다.

 

타이머 컴포넌트가 화면에 처음 그려질 때(마운트) timer을 시작하고

timer컴포넌트가 화면에 사라질 때(언마운트) 타이머를 정리 즉 끝내주는 코드를 작성한 것이다.

 

최종 코드

const UseEffect2 = () => {
    const [showTimer, setShowTimer] = useState(false);
    return (
        <div>
            {showTimer && <Timer />}
            <button onClick={() => setShowTimer(!showTimer)}>
                Toggle Timer
            </button>
        </div>
    );
};

const Timer = (props) => {
    useEffect(() => {
        const timer = setInterval(() => {
            console.log("타이머 돌아가는중...");
        }, 1000);

        return () => {
            clearInterval(timer);
            console.log("타이머가 종료되었습니다.");
        };
    }, []);

    return (
        <div>
            <span>타이머를 시작합니다. 콘솔을 보세요!</span>
        </div>
    );
};

 

 이 글은 별코딩 리액트 훅 강의를 보고 정리한 글입니다.

728x90
반응형

'React' 카테고리의 다른 글

[React] useMemo  (0) 2023.09.15
[React] useContext + Context API  (0) 2023.09.15
[React] useRef_DOM 요소 접근  (0) 2023.09.14
[React] useRef_변수 관리  (0) 2023.09.14
[React] useState  (0) 2023.09.11
Comments