일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 힛잇
- 스벨트
- jest
- queue
- javascript
- 자바스크립트
- data structure
- 이종호
- 계명대
- 자바스크립트 자료구조
- 호키도키
- 개발자
- 자료구조
- Hitit
- 리액트 예제
- hokeys
- Svelte
- 자스민
- IOS
- SWIFT
- HTML
- TDD
- 개발
- react
- 리액트
- 호키스
- 비동기
- 스위프트
- 계명대 이종호
- hokidoki
- Today
- Total
Dog foot print
useEffect 조금 더 자세히 사용해보기 본문
useEffect 란 ?
useEffect
함수는 렌더링 이후 컴포넌트와 외부의 시스템을 연결해주기 위해 만들어진 훅이다. 별도로 컨트롤이 가능하다면, “useEffect”를 사용해, 사이드 이펙트를 발생시키기도 한다.
useEffect Types
function useEffect(effect: EffectCallback, deps?: DependencyList): void;
//
type EffectCallback = () => (void | Destructor);
type DependencyList = ReadonlyArray<unknown>;
//
type Destructor = () => void | { [UNDEFINED_VOID_ONLY]: never };
useEffect는 첫번째 인자인 effect
는 렌더링 이후에 사용 할 콜백을 의미한다.. deps
는 의존성 목록으로 “effect”로 전달한 콜백 함수의 실행 조건을 의미한다.
마운트 시 사용하기
보통 컴포넌트가 “최초 렌더링 되었다”는 의미를 “ 마운트(Mount) 되었다. “라고 표현한다. 보통 컴포넌트가 렌더링 이후에 이루어지는 작업은 최초 렌더링시 필요한 API를 호출 하거나, 소켓연결이나 시간으로 제어해야하는 동작 같이 단 한번만 실행되어야 하는 작업들일 것이다.마운트시 사용하기 위해서는 의존성에 빈 배열 []
을 전달하면 된다.
아래의 예제는 함수형 컴포넌트에서 setInterval
을 호출해 1초 마다 자신의 상태를 1씩 증가시키는 잘못된 코드이다.
import React, { useState } from 'react'
export default function Effect_1() {
const [count, setCount] = useState(0)
setInterval(()=>{
setcount((prev) => prev + 1)
console.log("Update")
},1000)
return (
<div>
{count}
</div>
)
}
이 코드의 실행 결과는 다음과 같은데, 이는 setInterval에서 setCount를 호출 할 떄 마다 Effect_1 함수가 다시 실행되어 setInterval이 다시 호출되기 때문이다.
위의 예시처럼 렌더링이 발생될 때마다 실행되지 않고, 단 한번만 코드가 실행되어야 하는 경우 useEffect를 사용하고 빈 배열 []
을 전달하여 최초 한번 만 실행되게 끔 해야 한다.
아래 예시는 위의 코드를 useEffect
를 사용해 제어한 코드이다.
import React, { useState,useEffect } from 'react'
export default function Effect_1() {
const [count, setcount] = useState(0)
useEffect(() => {
setInterval(()=>{
setcount((prev) => prev + 1)
console.log("one second later");
},1000)
console.log("mount")
},[])
console.log("Update")
return (
<div>
{count}
</div>
)
}
setCount로 인하여, 함수가 렌더링 되어도 setInterval이 다시 호출 되지 않는다.
언 마운트시 사용하기
언 마운트(un mount)는 마운트(mount)의 반대되는 말로 컴포넌트가 더 이상 사용되지 않게 되었을 때를 의미한다. 언 마운트시 필요한 작업을 clean up
이라고 하는데, 이때는 소켓 연결의 해지나, 구독과 같은 것들을 해지하는 등과 같이 컴포넌트의 흔적을 없애는 행위들을 합니다
아래의 예시는 button을 클릭해, Effect_1을 화면에서 제거 했음에도 setInterval함수가 계속 동작하는 잘못된 코드입니다.
import React,{useRef, useState,useEffect} from 'react'
function Effect_1() {
const [count, setcount] = useState(0)
useEffect(() => {
setInterval(()=>{
setcount((prev) => prev + 1)
console.log("one second later");
},1000)
console.log("mount")
},[])
console.log("Update")
return (
<div>
{count}
</div>
)
}
export default function App() {
const [display,setDisplay] = useState(true);
return (
<div>
<button onClick={() => setDisplay((prev)=> !prev)}>Click</button>
{display ? <Effect_1/> : <></>}
</div>
)
}
버튼을 눌러 화면에서 “Effect_1” 컴포넌트를 제거 했음에도, setInterval
함수가 계속 실행되어 one second later
가 출력 됩니다. 다만 컴포넌트는 실제로 제거된 상태이기에 Update
는 더 이상 출력되지 않습니다.
이처럼 컴포넌트가 제거되어 있지만 함수 내에서 구독이나 지속적으로 동작하는 함수를 제거하지 않으면, 컴포넌트가 다시 마운트 되었을 때 이중으로 연결이 지속되게 되며, 함수내에서 스코프가 계속 유지되어 가비지 컬렉터가 연결된 변수들을 제거하지 못하게 됩니다.
아래는 mount때 정의되어 있는 setInterval함수를 "unmount" 시 제거하는 코드입니다.
import React,{useRef, useState,useEffect} from 'react'
function Effect_1() {
const [count, setcount] = useState(0)
useEffect(() => {
const myInterval = setInterval(()=>{
setcount((prev) => {
console.log(`${prev + 1} second later`);
return prev + 1
})
console.log("Mount")
},1000)
return () => {
clearInterval(myInterval)
console.log("Un mount")
}
},[])
console.log("Update")
return (
<div>
{count}
</div>
)
}
export default function App() {
const [display,setDisplay] = useState(true);
return (
<div>
<button onClick={() => setDisplay((prev)=> !prev)}>Click</button>
{display ? <Effect_1/> : <></>}
</div>
)
}
이제 Effect_1을 화면에서 제거 하였을 때 clearInterval이 작동하여, 더 이상 setInterval함수가 작동하지 않습니다.
의존성 목록 주입하기
useEffect는 특정 값이 변경될 때 마다 전달한 콜백 함수의 실행 시기를 결정 할 수 있습니다. 이 때 함수의 실행 조건에 해당하는 변수를 의존성이라 하며, 이 변수를 여럿 사용 할 수 있게 끔 배열에 전달하는데, 이 배열의 이름을 의존성 목록이라고 합니다.
아래는 위의 예제에서 count값이 짝수인지 홀수 인지에 따라 화면에 문구를 렌더링 기능을 추가한 예시이다.
import React,{useRef, useState,useEffect} from 'react'
function Effect_1() {
const messages = ["짝수입니다.", "홀수입니다."]
const [count, setcount] = useState(0)
const [message, setMessage] = useState(messages[0])
useEffect(() => {
const myInterval = setInterval(()=>{
setcount((prev) => {
console.log(`${prev + 1} second later`);
return prev + 1
})
console.log("Mount")
},1000)
return () => {
clearInterval(myInterval)
console.log("Un mount")
}
},[])
useEffect(()=>{
if(count % 2){
setMessage(messages[1])
}else{
setMessage(messages[0])
}
},[count])
return (
<div>
{count}
<div>{message}</div>
</div>
)
}
export default function App() {
const [display,setDisplay] = useState(true);
return (
<div>
<button onClick={() => setDisplay((prev)=> !prev)}>Click</button>
{display ? <Effect_1/> : <></>}
</div>
)
}
결과는 다음과 같습니다.
의존성 목록 사용시 주의해야 할 점
위의 예시에서는 useEffect
내에서 count 변수를 의존성으로 선택하여, 이 변수가 짝수인지 홀수인지에 따라 전달할 문자열과 함께 setMessage
를 호출 합니다. 그러나, 이와 같은 경우 setCount로 인하여, 렌더링이 발생하고 setMessage로 인하여 렌더링이 두번 발생합니다.
그러므로 useEffect
를 사용해서, 상태를 굳이 변경시키지 않아도 되는 작업은 useEffect 바깥에서 실행하는 것이 좋습니다.
function Effect_1() {
const messages = ["짝수입니다.", "홀수입니다."]
const [count, setcount] = useState(0)
useEffect(() => {
const myInterval = setInterval(()=>{
setcount((prev) => {
console.log(`${prev + 1} second later`);
return prev + 1
})
},1000)
console.log("Mount")
return () => {
clearInterval(myInterval)
console.log("Un mount")
}
},[])
console.log("render")
return (
<div>
{count}
<div>{count % 2 ? messages[1] : messages[0] }</div>
</div>
)
}
useEffect를 사용할 때 가장 조심해야하는 점은 의존성으로 인하여 “무한 루프”에 빠지지 않도록 조심하는 일입니다. useEffect에서 상태를 변경시킬때, 의존성 목록에 만약 같은 상태를 특정 상황일 때만 변경하지 않는다면 그 변경사항으로 인하여 다시한번 useEffect로 전달한 콜백이 실행될 것 입니다.
아래의 예제는 count
를 의존성 배열로 전달 한 뒤, 내부에서 count변수를 변경하는 잘못된 코드입니다.
function Effect_1() {
const messages = ["짝수입니다.", "홀수입니다."]
const [count, setcount] = useState(0)
useEffect(() => {
const myInterval = setInterval(()=>{
setcount((prev) => {
console.log(`${prev + 1} second later`);
return prev + 1
})
},1000)
console.log("Mount")
return () => {
clearInterval(myInterval)
console.log("Un mount")
}
},[])
useEffect(()=>{
setcount((prev) => prev + 1)
},[count])
console.log("render")
return (
<div>
{count}
<div>{count % 2 ? messages[1] : messages[0] }</div>
</div>
)
}
렌더링 시 마다 사용하기
useEffect의 deps
매개변수에 배열을 전달하지 않는다면 해당 useEffect는 렌더링이 발생할 때 마다 실행될 것입니다.
아래의 코드는 의존성 목록을 전달하지 않은 예시입니다.
function Effect_1() {
const messages = ["짝수입니다.", "홀수입니다."]
const [count, setcount] = useState(0)
useEffect(() => {
const myInterval = setInterval(()=>{
setcount((prev) => {
console.log(`${prev + 1} second later`);
return prev + 1
})
},1000)
console.log("Mount")
return () => {
clearInterval(myInterval)
console.log("Un mount")
}
},[])
useEffect(() => {
console.log("Render")
})
return (
<div>
{count}
<div>{count % 2 ? messages[1] : messages[0] }</div>
</div>
)
}
의존성을 전달하지 않은 useEffect
를 사용 할 때는 매우 조심해야 합니다. 무한 루프에 빠져 지속적으로 렌더링을 유발 시킬 수 있습니다. 렌더링과 무관한 작업을 하거나 정말 조심히 상태를 업데이트 해야합니다. 그러므로 의존성 목록과 함께 사용하는 것을 적극 권장합니다.
customHook
useEffect
도 외부에서 정의하고 컴포넌트 내에서 호출 할 수 있습니다. 주로 재활용성이 높은 함수들을 따로 모아 사용 합니다.
아래의 예제는 “count”의 이전 값을 저장하여 렌더링 된 이후 이전 count값을 사용 할 수 있는 예제입니다.
function customHook(value :number){
const prev = useRef(value)
useEffect(()=>{
prev.current = value
},[value]);
return prev.current
}
function Effect_1() {
const messages = ["짝수입니다.", "홀수입니다."]
const [count, setcount] = useState(0)
const prevCount= customHook(count);
useEffect(() => {
const myInterval = setInterval(()=>{
setcount((prev) => {
console.log(`${prev + 1} second later`);
return prev + 1
})
},1000)
console.log("Mount")
return () => {
clearInterval(myInterval)
console.log("Un mount")
}
},[])
return (
<div>
<div>
이전 값은 {prevCount}입니다.
</div>
{count}
<div>{count % 2 ? messages[1] : messages[0] }</div>
</div>
)
}
결과는 다음과 같습니다.
'REACT' 카테고리의 다른 글
useMemo 및 memo 조금 더 자세히 사용해보기 (0) | 2023.04.11 |
---|---|
useContext 조금 더 자세히 사용해보기 (0) | 2023.03.30 |
useState 조금 더 자세히 사용해보기 (0) | 2023.03.23 |
React resolve + css/sass + typescript 적용하기 (0) | 2023.03.15 |
React 시작 환경 구축하기 (0) | 2023.03.11 |