043. React - useEffect
useEffect란?
리액트에서 컴포넌트의 사이드 이펙트를 제어하는 훅.
사이드 이펙트는 직역하면 '부작용'이고, 리액트에서 표현하는 사이드 이펙트는 어떤 기능에서 '의도하지 않은 부수적인 효과', 내지 '파생되는 효과' 정도의 개념이다.
사용방법
- import하기
// state import 하기
import { useEffect } from "react";
- 원형 작성하기
const [count, setCount] = useState(0);
useEffect(()=>{},[]);
// 두 개의 인자를 받는데 첫번째는 콜백함수, 두번째는 의존성 배열(Dependency Array)
// 해석은 Deps의 값이 변경되면 콜백함수가 실행됨.
위와 같이 useState
훅과 동시에 사용 가능. 카운트 앱에서 사용되는 count state인데, setCount로 값이 변경될 때마다 useEffect 훅의 콜백함수를 실행시킬 수 있다.
- 사용하기
const [count, setCount] = useState(0);
useEffect(()=>{
console.log(`카운트가 ${count}으로 변경되었습니다.`);
},[count]);
// 이렇게 작성하면 count state가 변경될 때마다 콘솔에 출력됨.
- Deps에 여러 개 값 추가하기
Deps는 배열이기 때문에 여러 개의 값의 변화를 감지할 수 있다.
const [count, setCount] = useState(0);
const [input, setInput] = useState('');
useEffect(()=>{
console.log(`카운트가 ${count}으로 변경되었습니다. 현재 입력필드의 값은 ${input}입니다.`);
},[count, input]);
return (
<>
<input value={input} onChange={(e)=>setInput(e.target.value)}/>
</>
)
useState 훅을 바로 출력하지 않고 useEffect 훅을 사용해야 하는 이유
const [count, setCount] = useState(0);
const [input, setInput] = useState('');
return (
<>
<input value={input} onChange={(e)=>
setInput(e.target.value);
console.log(count);}/>
</>
)
예를 들어서 위처럼 useEffect
훅을 이용하지 않고 count state의 변화를 직접 console에서 찍어볼 수 없는지 의문이 든다.
그런데 실제로 저 코드를 콘솔에 출력해보면 카운트가 증가되는 것이 한 박자 늦게 뜬다. 그러니까 나는 카운트를 1 증가 시켰는데 0이 출력되고, 2가 되어야 하는데 1이 출력된다는 것이다.
그 이유는 useState
훅은 비동기적으로 실행되기 때문이다. 즉 코드의 흐름대로 작성 순서대로 실행되는 것이 아니라 1. 화면에 렌더링 되는 부분부터 렌더링(return문 내부)
-> 2. 자바스크립트 함수부터 실행(console.log)
-> 3. 가장 마지막에 useState훅 실행
순서대로인 것이다. useState
훅은 코드 흐름대로 Mount만 되지 실행은 가장 마지막에 한꺼번에 모아서 처리하기 때문에 이미 콘솔에 찍히고 나서 카운트가 증가하는 현상, 즉 한 박자 늦게 나오는 현상이 발생한다.
따라서 같은 시점에서 같이 비동기적으로 실행되는 useEffect
훅을 사용하면 useState
의 사이드 이펙트, 즉 상태 변화를 즉각 감지할 수 있다.
useEffect로 라이프사이클 제어하기
Mount (탄생) 시에 useEffect 호출하기
사용 방법
deps에 빈 배열을 주면 됨.
useEffect(()=>{
console.log('마운트 되었습니다');
},[]);
이렇게 하면 마운트 시에만 한 번 실행하고, 그 뒤로 컴포넌트가 업데이트 되더라도 useEffect
훅은 실행되지 않음.
웹 사이트를 개발할 때 이런 방식은 다양한 상황에서 쓰일 수 있는데, 대표적으로는 API 호출이 있음. 컴포넌트가 재 렌더링 될 때마다 API를 새롭게 호출하면 서버에 부하가 걸릴 수 있기 때문에 이럴 때 useEffect
훅을 이용하면 컴포넌트를 처음 마운트 했을 때만 API 호출을 일으킬 수 있음.
활용 예시
// useEffect 사용 예시 : API 호출하기
import { useState, useEffect } from 'react';
import axios from 'axios';
function DataFetchingComponent() {
const [data, setData] = useState(null);
useEffect(() => {
axios.get('/api/data')
.then(response => {
setData(response.data);
})
.catch(error => {
console.error('Error fetching data:', error);
});
}, []);
return (
<div>
{data ? <pre>{JSON.stringify(data, null, 2)}</pre> : 'Loading...'}
</div>
);
}
export default DataFetchingComponent;
// useEffect 사용 예시 : 타이머를 마운트 시 작동시키고 언 마운트 시 해제 시키기
import { useEffect } from 'react';
function TimerComponent() {
useEffect(() => {
const timer = setInterval(() => {
console.log('Timer tick');
}, 1000);
return () => clearInterval(timer);
}, []);
return <div>콘솔에서 타이머를 확인하세요.</div>;
}
export default TimerComponent;
// useEffect 사용 예시 : 웹소켓 연결이나 외부 데이터 소스에 대한 서브스크립션을 설정하고, 컴포넌트가 언마운트될 때 이를 해제하는 경우
import { useEffect } from 'react';
function WebSocketComponent() {
useEffect(() => {
const socket = new WebSocket('ws://example.com/socket');
socket.onmessage = (event) => {
console.log('응답 메시지:', event.data);
};
return () => {
socket.close();
};
}, []);
return <div>웹소켓 연결이 활성화되었습니다. 콘솔에서 확인해보세요.</div>;
}
export default WebSocketComponent;
Update (업데이트) 시에 useEffect 호출하기
두 가지 상황이 있다.
- 마운트 시에 한 번 실행은 하고, 업데이트 마다 실행하는 방법
- 마운트 시에 실행하지 않고, 업데이트 때만 실행하는 방법
일단 두 상황 다 useEffect의 두번째 매개변수인 deps를 빈 배열도 아닌 아예 작성하지 않으면 된다.
// 마운트 시에 한 번 실행 + 업데이트 때마다 실행
useEffect(() => {
console.log('업데이트 되었습니다.');
});
위 상황은 카운트가 계속 증가한다든지, 사용자가 Input에 타이핑을 한다든지 하는, 재 렌더링이 계속 되는 상황에서 상태가 바뀔 때마다 계속 실행된다. 물론 최초 마운트 시 1회는 같이 실행된다.
// 마운트 시에는 실행하지 않고 업데이트 할때만 실행
// useRef 훅 Import
import { useEffect, useRef } from "react";
// useRef로 컴포넌트 마운트 상태 관리
const isMount = useRef(false);
// 조건식으로 마운트 됐을 때만 함수 코드블럭을 실행하도록 설정
useEffect(() => {
if(!isMount.current) {
isMount.current = true;
return;
}
console.log('업데이트 되었습니다.');
});
// isMount의 값이 false이면 isMount의 값을 true로 바꾸고
// 함수를 더 진행시키기 않고 return 해버리기
// 다음부터는 컴포넌트가 재 렌더링 될 때는 isMount가 이미 마운트 될 때
// true로 바뀌었기 때문에 조건문을 통과하고 코드가 업데이트 때마다 실행된다.
useRef란?
useRef
또한 React의 hook 중 하나로 current
라는 프로퍼티를 가진 객체를 반환하는 훅이다. 컴포넌트의 라이프사이클 동안만 값이 유지된다는 특징이 있고, 실제 웹 개발에서는 1. DOM의 특정 요소에 접근하거나 2. 값의 변경을 추적할 때 사용한다.
1. DOM의 특정 요소에 접근
// hook import 하기
import { useEffect, useRef } from "react";
function TextInput() {
// 초기 값 null로 할당
const inputRef = useRef(null);
useEffect(()=>{
// TextInput 컴포넌트가 마운트 될 때 inputRef에 포커싱 시키기
inputRef.current.focus();
},[]);
return (
<>
{/* HTML에서 ID 부여하듯 ref 속성으로 useRef훅을 할당한 변수를 바인딩 */}
<input ref={inputRef} type="text"/>
</>
)
}
export default TextInput;
2. 값의 변경을 추적하기
useRef
는 상태가 변경되어 컴포넌트가 재 렌더링 되더라도 재 렌더링 되지 않고 값이 유지되기 때문에 카운트 기능 같은 것에 사용하면 유용하다.
import { useRef, useState, useEffect } from 'react';
function Timer() {
// 초기값 null로 설정하여 변수에 할당
const timerRef = useRef(null);
const [count, setCount] = useState(0);
// 마운트 되고, 업데이트 될 때마다 실행할 useEffect 훅 정의 (deps 빈 배열)
useEffect(() => {
timerRef.current = setInterval(() => {
setCount(prevCount => prevCount + 1);
}, 1000);
return () => clearInterval(timerRef.current);
}, []);
return <div>Timer: {count}</div>;
}
export default Timer;
Unmount (죽음) 시에 useEffect 사용하기
마운트에 실행하는 것처럼 빈 배열을 두번째 매개변수로 전달해주고, useEffect
훅의 콜백함수로 Clean Up (정리) 함수를 사용하면 됨.
import { useEffect } from "react";
function Even() {
useEffect(()=>{
return () => {
// return문 안에 작성하는 것이 클린업 함수임.
}
},[])
return (
<>
<div> 짝수입니다. </div>
</>
)
}
이것은 언제 사용하냐면, 조건부 렌더링을 할 때 사용함. 예를 들어서 위는 짝수일 때만 카운트가 보여주는 컴포넌트인데, 카운트가 홀수일 땐 사라져야 하기 때문임.
실제 웹 개발 시에는 서버로부터 데이터를 API 호출로 받아오는 동안은 스피너 같은 로딩 컴포넌트를 띄워주고, fetch가 완료되면 Unmount 시키는 조건부 렌더링을 할 수도 있음.
import React, { useState, useEffect } from 'react';
function DataFetchingComponent() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/data')
.then(response => response.json())
.then(data => {
setData(data);
setLoading(false);
});
}, []);
if (loading) {
return <div>Loading...</div>;
}
return (
<div>
<h1>Data:</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
export default DataFetchingComponent;
'Programing > React' 카테고리의 다른 글
048. React - Context API 상태관리 (0) | 2024.05.28 |
---|---|
112. [MAX] Styled-Components 기본 사용법 (0) | 2024.05.23 |
042. React - 라이프사이클 (0) | 2024.05.23 |
052. React - 페이지 라우팅 - 동적 경로 (0) | 2024.05.23 |
051. React - 페이지 라우팅 - 페이지 이동 (0) | 2024.05.23 |
댓글