React 컴포넌트의 생애 주기 (생명 주기)
탄생(화면에 나타나는 것 Mount) -> 변화(업데이트(리렌더) Update) -> 죽음(화면에서 사라짐 UnMount)
React는 기본적으로 Lifecycle마다 실행할 수 있는 메서드를 가지고 있다.
다만 위의 메서드들은 class형 컴포넌트에서만 사용할 수 있다.
// 함수형 컴포넌트에서 Lifecycle를 제어하기 위해서는 useEffect라는 React Hooks를 사용해야 한다.
useEffect(() => {
// todo... // Callback 함수
}, []) // [] => Dependency Array(의존성 배열) 이 배열 내에 들어있는 값이 변화하면 콜백 함수가 수행된다.
useEffect(() => {
// Dependency Array가 있을 경우 최초 렌더 됐을때만 아래의 'Mount!'가 출력된다. 리렌더X
console.log('Mount!');
}, []);
useEffect(() => {
// Dependency Array를 전달하지 않을 경우 리렌더링 될 때마다 'Update!'가 출력된다.
console.log('Update!');
});
const [count, setCount] = useState(0);
useEffect(() => {
// console.log(`count is update : ${count}`);
if(count > 5) {
alert('count가 5를 넘었습니다. 따라서 1로 초기화 됩니다.');
setCount(1);
}
}, [count]); // Dependency Array의 값이 변경되면 콜백 함수가 수행된다.
Dependency Array([])가 비어 있을 경우 최초 렌더 되었을 경우만 실행
Dependency Array([])가 없다면 리렌더링 될때마다 실행
Dependency Array([value])에 값이 전달된다면 그 값이 변경될때 실행
// 부모 컴포넌트
import { useState } from "react"
import Products from "./components/Products";
export default function AppProducts() {
const [showProducts, setShowProducts] = useState(true);
return (
<div>
// showProducts가 true일 경우만 Products컴포넌트를 보여준다.
{showProducts && <Products/>}
// Toogle 기능 구현
<button onClick={() => setShowProducts((show) => !show)}>Toogle</button>
</div>
)
}
아래와 같이 fetch를 하게 되면 setProducts가 Products 함수를 계속 호출하면서 무한 루프에 빠진다.
// 자식 컴포넌트
import { useEffect, useState } from "react"
export default function Products() {
const [count, setCount] = useState(0);
const [products, setProducts] = useState([]);
// 무한 루프에 빠진다.
fetch('data/products.json')
.then(res => res.json())
.then(data => {
setProducts(data);
});
return (
<>
<ul>
{products.map(elm => (
<li key={elm.id}>
<article>
<h3>{elm.name}</h3>
<h3>{elm.price}</h3>
</article>
</li>
))}
</ul>
<button onClick={() => setCount(prev => prev + 1)}>{count}</button>
</>
)
}
// 자식 컴포넌트
import { useEffect, useState } from "react"
export default function Products() {
const [count, setCount] = useState(0);
const [products, setProducts] = useState([]);
useEffect(() => {
fetch(`data/${checked ? 'sale_' : ''}products.json`)
.then(res => res.json())
.then(data => {
setProducts(data);
});
return () => {
console.log('🧹 깨끗하게 청소하는 일들을 합니다.')
}
// Dependency Array를 빈 배열로 넣어주게 되면 컴포넌트가 보여지는 최초에만 네트워크 요청을 한다.
}, []);
return (
<>
<ul>
{products.map(elm => (
<li key={elm.id}>
<article>
<h3>{elm.name}</h3>
<h3>{elm.price}</h3>
</article>
</li>
))}
</ul>
<button onClick={() => setCount(prev => prev + 1)}>{count}</button>
</>
)
}
만약 컴포넌트가 없어질때 무언가 정리(메모리 정리, 소켓 네트워크 통신 닫기...)해야 한다면
useEffect에 return 함수를 전달해주면 된다.
useEffect(() => {
fetch(`data/${checked ? 'sale_' : ''}products.json`)
.then(res => res.json())
.then(data => {
setProducts(data);
});
// 이 콜백함수는 컴포넌트가 없어질때 즉 화면에서 사라질때(Unmount) 호출되는 함수다.
return () => {
console.log('🧹 깨끗하게 청소하는 일들을 합니다.')
}
}, []);
어떤 특정한 값이 변경될 때만 네트워크 요청을 하고 싶은 경우
import { useEffect, useState } from "react"
export default function Products() {
const [count, setCount] = useState(0);
const [products, setProducts] = useState([]);
// checked 초기값 셋팅
const [checked, setChecked] = useState(false);
// 상태관리 함수
const handleChange = () => setChecked(prev => !prev);
useEffect(() => {
// checked가 true일 경우 세일 정보 json 요청
// checked가 false일 경우 일반 정보 json 요청
fetch(`data/${checked ? 'sale_' : ''}products.json`)
.then(res => res.json())
.then(data => {
setProducts(data);
});
// 컴포넌트가 화면에서 사라질때 실행되는 콜백함수
return () => {
console.log('🧹 깨끗하게 청소하는 일들을 합니다.')
}
// checked의 값이 변경될때마다 네트워크 요청을 하게 된다.
}, [checked]);
return (
<>
// value prop으로 전달하는 checked의 초기값은 false다.
// onChange가 발생할때마다 handleChange를 참조해 참 or 거짓으로 변경한다.
<input id="checkbox" type="checkbox" value={checked} onChange={handleChange} />
<label htmlFor="checkbox">Show Only 🛎️ Sale</label>
<ul>
{products.map(elm => (
<li key={elm.id}>
<article>
<h3>{elm.name}</h3>
<h3>{elm.price}</h3>
</article>
</li>
))}
</ul>
<button onClick={() => setCount(prev => prev + 1)}>{count}</button>
</>
)
}
// Error 발생!
return (
<>
<input id="checkbox" type="checkbox" value={checked} onChange={handleChange} />
<label htmlFor="checkbox">Show Only 🛎️ Sale</label>
<ul>
// 아래와 같이 자식 요소들을 만들때 고유한 key값이 없으면 Error가 발생한다.
// 아래의 경우는 json파일에 id key가 없어서 Error가 발생했다.
{products.map(elm => (
// key값 전달!!
<li key={elm.id}>
<article>
<h3>{elm.name}</h3>
<h3>{elm.price}</h3>
</article>
</li>
))}
</ul>
<button onClick={() => setCount(prev => prev + 1)}>{count}</button>
</>
)