useEffect
- 사이드 이펙트를 잘 다루기 위한, Hook
- API 싱크를 위해 사용 가능
사이드 이펙트
- 앱이 제대로 동작하기 위해 실행되어야 하지만 컴포넌트 렌더링 중 직접적인 영향을 미치지 않는 작업
주의 할 점
- 모든 부수 효과에 대한 과한 사용은 좋지 않음 ex) localStorage에 있는 item을 가져와서 상태값을 추가 하는 경우에 useEffect를 쓰는 등.. localStorage의 item은 위의 코드의 위치를 가져오는 함수와 달리 콜백 함수 없이 즉각적으로 데이터를 불러오기 때문에 굳이 useEffect를 쓰지 않고 직접 useState초기 값으로 설정 해도 됨.
- 컴포넌트 렌더링이 끝난 후 추가적으로 실행되는 방식이라서 남용하면 성능 낭비가 될 수 있음
설명 및 실행 방법
import {useEffect, useState} from 'react';
export default function Test(){
const [locationInfo, setLocationInfo] = useState({
latitude : null,
longtitude :null,
});
useEffect(() => {
navigator.geolocation.getCurrentPosition((position)=>{
const Info ={
latitude : position.coords.latitude,
longtitude :position.coords.longtitude,
}
});
setLocationInfo(Info)
},[])
}
설명
useEffect 내에는
- 현재 위치를 받아와서 위치 정보를 담는 코드가 구현되어있음 => 이 코드가 만약 useEffect 없이 실행되게 되면 useState값이 계속 바뀌기 때문에 무한 렌더링이 될것임 => 따라서 useEffect를 사용해야 하는 것
방법
첫번째 인자 - 실행할 함수를 작성 해줌 , 함수 실행 시점은 JSX 코드가 반환된 후 (즉시 실행 X)
두번째 인자
- 의존성 배열 정의
- 의존성 값이 변화했을 경우에 한해 함수 재 실행
- 만약 비어있으면 최초 한번만 실행 (의존성이 없기에 바뀔 값이 없으니 재실행 될 이유도 없음)
- 의존성 배열을 빠뜨리면 무한 렌더링 되니 주의
- props나 state를 useEffect 내에서 사용 하는 경우 의존성에 추가 !!!!!
다른 사례
부모 컴포넌트
export default Parent function(){
const [modalIsOpen, setModalIsOpen] = useState(false);
function handleClick(){
setModalIsOpen((prev)
=> {
return !prev
})
}
return (
<>
<Modal open={modalIsOpen}><p>hi</p></Modal>
<button onClick={handleClick}>click</button>
</>
)
}
자식 컴포넌트
fucntion Modal({open, children}){
const dialog = useRef();
useEffect(() =>{
if(open){
dialog.current.showModal();
}else{
dialog.current.close();
}
},[open])
return createPortal(<dialog className="modal" ref={dialog}>
{children}
</dialog>, document.getElementById("modal")
)
}
=> 위와같이 Modal을 여닫을 때 사용 가능 하다. 부모 컴포넌트에서 내려 받은 open값이 의존성 배열에 들어가 있기 때문에 open값이 바뀔 때 마다 useEffect 실행 가능
다른 사례2
부모 컴포넌트
export default Parent function(){
const [modalIsOpen, setModalIsOpen] = useState(false);
function handleClick(){
setModalIsOpen((prev)
=> {
return !prev
})
}
function closeModal(){
setModalIsOpen(false)
}
return (
<>
<Modal clickHandler={closeModal} open={modalIsOpen}><p>hi</p></Modal>
<button onClick={handleClick}>click</button>
</>
)
}
자식 컴포넌트
fucntion Modal({clickHandler,open, children}){
useEffect(()=>{
const timer = setTimeout(()=>{
clickHandler(); // 3초 지나면 자동으로 open = false하는 함수
},3000)
return () => {
clearTimeOut(timer);
} // 다른 함수 반환 가능, 위의 함수가 다시 작동하기 바로전
// or 컴포넌트가 사라지기 전 Dom에서 사라지기 전
// Dom에서 삭제되기 전에 timer 삭제
// Effect함수가 최초로 작동되기 바로 전에는 작동 X
},[clickHandler])
useEffect(() =>{
if(open){
dialog.current.showModal();
}else{
dialog.current.close();
}
},[open])
return createPortal(<dialog className="modal" ref={dialog}>
{children}
</dialog>, document.getElementById("modal")
)
}
=> Modal 내에는 3초 후면 modal이 자동으로 닫히는 코드가 추가됨, 하지만 3초가 되기 전에 click 버튼을 눌렀을 때에 상태값이 제대로 동작하지 못할 수 있음 따라서 위에 처럼 useEffect에 return 함수를 추가하면 함수가 재 작동하기 바로 직전이나, Dom에서 사라지기 전에 함수 실행 가능.
코드의 문제점 : 위의 문제점은 clickHandler와 같은 함수를 의존성으로 추가 할 경우 함수는 객체이기 때문에 컴포넌트가 재 생성 될때마다 값이 바뀌어 무한 루프 생성 될 위험이 있음
코드 해결 : UseCallback();
- 컴포넌트가 재렌더링 될 때마다 함수가 재 생성되지 않게 함. 대신 메모리로서 내부에 저장
- memo, useEffect 사용할 때 필요 할 수 있음
해결 한 부모 컴포넌트
import {useRef, useState, useEffect, useCallback} from 'react';
export default Parent function(){
const [modalIsOpen, setModalIsOpen] = useState(false);
function handleClick(){
setModalIsOpen((prev)
=> {
return !prev
})
}
const closeModal= useCallback( function closeModal(){
setModalIsOpen(false)
},[]);
return (
<>
<Modal clickHandler={closeModal} open={modalIsOpen}><p>hi</p></Modal>
<button onClick={handleClick}>click</button>
</>
)
}
useCallback 사용방법
첫번째 인자 - 사용할 함수
두번째 인자 - 의존성, 첫번째 인자에서 사용하는 prop, state value값 추가
'React' 카테고리의 다른 글
[React] memo, useMemo (0) | 2024.04.11 |
---|---|
[React] useContext (0) | 2024.04.08 |
[React] Refs, Portals (0) | 2024.04.04 |
[React] React Developer Tools (0) | 2024.04.03 |
[React] 정적인 컴포넌트를 보관할 때 (0) | 2024.04.02 |