useEffect는 Hooks 에서 가장 중요한 개념이다 !
기본적으로 생김새는 아래 코드와 같이 생겼고, 콜백함수를 받는다.
콜백함수 내부에는 내가 원하는 코드를 작성해주면 됨 !
useEffect 코드는 두가지 작성법이 있는데,
첫번째는 렌더링이 될 때마다 실행되고
useEffect(()=>{
//작업 ..
});
두번째는 배열 (dependency array)을 받는데, 화면에 첫 렌더링이 될 때 + 배열 안의 value 값이 바뀔 때 실행되는 형식이다.
useEffect(()=>{
//작업 ..
},[value]);
빈 배열을 전달받는다면, 그냥 화면에 첫 렌더링이 될 때만 실행된다.
useEffect(()=>{
//작업 ..
},[]);
이벤트를 만들었는데, 더이상 그 이벤트가 필요가 없다면 제거해주는 정리 작업(cleanup) 을 해줘야하는데 그럴 때 useEffect return으로 처리를 해주면 됨 !
(뒤에 타이머 예제로 더 알아보겠슴)
이렇게 함수를 return 해주면 해당 컴포넌트가 unmount 될 때, 혹은 다음 렌더링때 불릴 useEffect 가 실행되기 전에 return 내 함수가 실행이 된다!
update 버튼을 누를때마다 count 가 update 되게 만들어보자.
useEffect 를 사용하기 위해서는 우선 useState처럼 import 해와야 한다.
import { useState, useEffect } from "react";
function App() {
const [count, setCount] = useState(1);
const handleCountUpdate = () => {
setCount(count + 1);
};
// 렌더링 될 때마다 매번 실행됨
useEffect(() => {
console.log("렌더링");
//컴포넌트가 화면에 렌더링 된 직후에 콘솔에 찍히는 것
});
return (
<div>
<button onClick={handleCountUpdate}>Update</button>
<span>count : {count}</span>
</div>
);
}
먼저 const [count, setCount] = useState(1);
로 count를 업데이트 할 state를 만들어주고, button 을 클릭할 때마다 handleCountUpdate
함수가 동작하게 작성해주었다.
그리고 useEffect 를 작성해 주었는데 컴포넌트가 화면에 렌더링 된 직후에 콘솔에 "렌더링" 이 찍히게 해보았다.
useEffect(() => {
console.log("렌더링");
});
자 이번에는 아래 이미지처럼 input 값을 입력했을 때 바로 출력되게끔 한 번 만들어 보겠음
위 코드에서 이 코드들을 추가해서 만들어 줬음 .
const [name, setName] = useState("");
const handleInputChange = (e) => {
setName(e.target.value);
};
<input type="text" value={name} onChange={handleInputChange} />
<span>name : {name}</span>
이제 input창에 값을 입력할 때 마다 useEffect 안의 있는 콜백이 계속 불리는데,
렌더링 될 때마다 콘솔에 찍어보며 확인헤보자
렌더링 옆에 붙어있는 숫자가 콜백이 계속 불려오는 숫자인데,
이렇게 매번 값을 업데이트 할 때마다 useEffect가 불려오는것? 넘 비효율적이다.
무거운게 불려오면 성능에도 좋지 못함.
그냥 name 업데이트는 무시하고 count 업데이트 때만 useEffect 를 실행시키고 싶다면?
바로
useEffect(()=>{
//작업 ..
},[value]);
뒤에 배열을 받는 방법이다.
useEffect(() => {
console.log("렌더링");
//컴포넌트가 화면에 렌더링 된 직후에 콘솔에 찍히는 것
},[count]);
지금 작성하고 있는 코드에서는 count 만 받고 싶으니깐, [count]를 배열로 받아옴!
이제 이걸 실행하면은 첫 렌더링 때와 , update 버튼을 눌렀을 때만 count가 업데이트 될 때만 렌더링 되는 것 !!!
각각의 변화마다 useEffect 를 다르게 줘보면
// 렌더링마다 매번 실행됨 - 렌더링 이후
useEffect(() => {
console.log("렌더링!");
});
// 마운트 + count 변화할 때마다 실행
useEffect(() => {
console.log("count 변화");
}, [count]);
// 마운트 + name 변화할 때마다 실행
useEffect(() => {
console.log("name 변화");
}, [name]);
이렇게 각각의 요소 값이 업데이트 될때 useEffect 가 실행이 됨을 볼 수 있음
근데 저 렌더링을 처음 실행할 때만 보여주고 싶다면?????
=> 빈 배열값을 넣어주면 됩니다.
useEffect(() => {
console.log("마운팅");
}, []);
짠
여기까지 최종 코드
import { useState, useEffect } from "react";
function App() {
const [count, setCount] = useState(1);
const [name, setName] = useState("");
const handleCountUpdate = () => {
setCount(count + 1);
};
const handleInputChange = (e) => {
setName(e.target.value);
};
// 렌더링마다 매번 실행됨 - 렌더링 이후
useEffect(() => {
console.log("렌더링!");
});
// 마운트 + count 변화할 때마다 실행
useEffect(() => {
console.log("count 변화");
}, [count]);
// 마운트 + name 변화할 때마다 실행
useEffect(() => {
console.log("name 변화");
}, [name]);
//처음 마운팅될때만 실행
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;
clean up 에 대해 알아보자.
우선 src 파일 안에 components 파일을 만들고, Timer.jsx 를 만들어 준다.
import React, { useEffect } from "react";
const Timer = (props) => {
useEffect(() => {
const timer = setInterval(() => {
console.log("타이머 돌아가는중");
}, 1000);
}, []);
return (
<div>
<span>타이머를 시작합니다. 콘솔창을 봐주십셔</span>
</div>
);
};
export default Timer;
그리고 App.js 에 위에서 만들어준 Timer 을 import 해주고 코드작성 시작!
import { useState, useEffect } from "react";
import Timer from "./components/Timer";
function App() {
const [showTimer, setShowTimer] = useState(false);
return (
<div>
{/* showTimer 이 true 일때만 Timer 보여주기 */}
{showTimer && <Timer />}
<button onClick={() => setShowTimer(!showTimer)}>Toggle Timer</button>
</div>
);
}
export default App;
지금 여기 코드 보면 useState 가 false 이다.
const [showTimer, setShowTimer] = useState(false);
아까 위에서 Input 넣을때는 값이었는데 이번에는 boolean 으로 준 것 .
여기서 원하는건
showTimer이 true 면 false 혹은 그 반대로 바뀌게 toggle .
<button onClick={() => setShowTimer(!showTimer)}>Toggle Timer</button>
setShowTimer(!showTimer) 이걸통해 showTimer이 true 면 false 혹은 그 반대로 바뀌게 해줌
근데 여기서 문제.
Toggle Timer 버튼을 꺼도 타이머가 계속 돌아감!
타이머가 끝났을 때 타이머를 종료해주기 위해서는
Timer.jsx 에서 Timer 함수에 clearInterval()
함수를 추가해줘야 한다.
이 clearInterval
이 타이머를 정리하는, 그러니깐 끝내주는 아이라고 보면 된다!
import React, { useEffect } from "react";
const Timer = (props) => {
useEffect(() => {
const timer = setInterval(() => {
console.log("타이머 돌아가는중");
}, 1000);
return () => {
clearInterval(timer);
console.log("타이머가 종료되었습니다.");
};
}, []);
return (
<div>
<span>타이머를 시작합니다. 콘솔창을 봐주십셔</span>
</div>
);
};
export default Timer;
타이머와 같은 함수를 쓸때는 setInterval() & clearInterval() 세트라고 생각하기 !