react 16.8 버전에 새로 도입된 기능이다.
대표적으로 함수형 컴포넌트에서도 상태 관리를 할 수 있는 useState, 렌더링 직후 작업을 설정하는 useEffect 등이 있다.
useState 는 가장 기본적인 hook이다. useState의 파라미터로 상태의 기본 값을 넣어준다.
setValue를 통해 value state를 변경해줄 수 있다.
import React, {useState} from 'react'
const Counter = ()=> {
const [value, setValue] =useState(0);
return (
<div>
<p>
현재 카운터 값은 <b>{value}</b>입니다
</p>
<button onClick={()=> setValue(value+1)}>+1</button>
<button onClick={()=> setValue(value-1)}>-1</button>
</div>
)
}
export default Counter
const [value, setValue] = useState(0)
, const [fruit, setFruit] = useState('banana')
와 같이 대괄호를 이용하여 state 변수를 선언한다.
이와 같은 자바스크립트 문법을 배열 구조 분해라고 한다.
🧐 *배열 구조 분해 문법에 대한 간략한 설명 ( 자세한 내용은 링크를 통해 MDN에서 확인해보자 )
배열 구조 분해는 배열이나 객체의 속성을 해체해 그 값을 개별 인수에 담을 수 있도록 하는 JS 표현식이다.
var foo = ["one", "two", "three"];
var [red, yellow, green] = foo;
console.log(red); // "one"
console.log(yellow); // "two"
console.log(green); // "three"
변수 선언이 분리 되어 있어도 구조 분해를 통해 값을 할당 할 수 있다.
["one", "two", "three"] = [red, yellow, green]
this.state ={
name:"",
age:0,
}
const {name, age} = this.state;
클래스형 컴포넌트의 componentDidMount와 componentDidUpdate, componentWillUnmount를 합친 형태로 볼 수 있다.
import { useEffect, useState } from "react"
const Info =()=>{
const [name, setName] = useState('')
const [nickname, setNickname] = useState('')
useEffect(()=>{
console.log('렌더링이 완료되었습니다.');
console.log({
name, nickname
})
})
const onChangeName =(e)=>{
setName(e.target.value)
}
const onChangeNickname =(e)=>{
setNickname(e.target.value)
}
return(
<div>
<>
<input value={name} onChange ={onChangeName}/>
<input value={nickname} onChange ={onChangeNickname}/>
</>
<div>
<b>이름 : </b> {name}
</div>
<div>
<b>닉네임 : </b> {nickname}
</div>
</div>
)
}
export default Info;
해당 코드를 실행 시켜보면 input value가 바뀔때 마다 다시 렌더링 되면서 useEffect가 실행되는 것을 console.log를 통해 확인할 수 있다.
componentDidMount, componentDidUpdate와는 달리 useEffect에서 사용되는 effect는 브라우저가 화면을 업데이트 하는 것을 차단하지 않는다. -> 애플리케이션 반응성 향상
레이아웃 측정과 같은 동기적 실행이 필요한 경우에는 useEffect와 동일한 API를 사용하는 useLayoutEffect라는 별도의 Hook이 있다.
useEffect에서 설정한 함수를 컴포넌트가 화면에 맨 처음 렌더링 될 때만 사용하고 업데이트될 때는 실행하지 않으려면 함수의 두 번째 파라미터로 비어 있는 배열을 넣어주면 된다.
위 예시 코드를 수정해보자.
...
useEffect(()=>{
console.log('마운트 될 때만 실행됩니다. ');
console.log({
name, nickname
})
}, [])
...
👉 실행결과
코드를 실행하면 처음 마운트 되었을때 console.log가 찍히고 이름 input 값으로 안녕하세요를 입력해도 useEffect 함수가 실행되지 않는다.
특정 값이 변경될 때마다 호출하고 싶은 경우에는 두번째 파라미터로 전달되는 배열에 검사하고자 하는 값을 넣어주면 된다. ( 위에서 빈 배열을 넣었던 부분에! )
만약 클래스형 컴포넌트라면 다음과 같이 작성된다.
...
componentDidUpdate(prevProps, prevState){
if(prevProps!==prevState){
doSomething()
}
}
...
위와 같은 작업을 useEffect로 구현해보자.
name을 배열에 넣어주고 name 값이 변화될 때마다 실행되도록 해보자.
...
useEffect(()=>{
console.log('name이 변경되었습니다. ');
console.log({
name, nickname
})
}, [name])
...
👉 실행결과
검사하고 싶은 값을 배열에 넣어줄때는 useState를 통해 관리되는 state도 넣을 수 있고, props로 전달받은 값을 넣어줄 수도 있다.
✨ useEffect는 기본적으로 렌더링되고 난 직후마다 실행되고, 두 번째 파라미터 배열에 무엇을 넣는지에 따라 실행되는 조건이 달라진다.
컴포넌트가 언마운트되기 전이나 업데이트되기 직전에 어떤 작업을 수행하고 싶다면 useEffect에서 cleanup함수를 반환해 주어야 한다.
useEffect(()=>{
console.log('name이 변경되었습니다. ');
console.log({
name, nickname
})
return()=>{
console.log('clean up')
console.log(name)
}
}, [name])
코드를 실행시키면 useEffect를 담고 있는 컴포넌트가 화면에 렌더링 되었을때 'name이 변경되었습니다'가 console.log로 출력되고, 컴포넌트가 화면에서 사라졌을 때 'clean up'이 출력된다.
언마운트 될 때만 cleanup 함수를 호출하고 싶다면 useEffect 두 번째 파라미터에 빈 배열을 넣어주면 된다.