useEffect는 함수형 컴포넌트에서 side effect 처리를 위해 사용된다. 그리고 class형 컴포넌트의 componentdidmount
, componentdidupdate
와 같은 역할을 함수형 컴포넌트에서 해준다.
클래스형 컴포넌트
import React,{component} from 'react'
class ClassCounterOne extends Component{
constructor(props){
super(props)
this.state={
count:0
}
}
componentDidMount(){
document.title=`Clicked ${this.state.count} times`
}
componentDidUpdate(prevProps,prevState){
document.title=`Clicked ${this.state.count} times`
}
render(){
return(
<div>
<button onClick={()=>this.setState({count:this.state.count +1})}>
Click{this.state.count}times
</button>
</div>
)
}
}
export default ClassCounterOne
함수형 컴포넌트
import React,{useState} from 'react'
function HookCounterOne(){
const [count,setCount]=useState(0)
useEffect(()=>{
document.title=`you clicked ${count} times`
})
return(
<div>
<button onClick={()=>setCount(count+1)}> Click {count} times </button>
</div>
)
}
export default HookCounterOne
useEffect는 컴포넌트 모든렌더 후에 실행된다! 첫번째 렌더 후 그리고 모든 업데이트 후에 실행되는 것이 useEffect
import React, {useState,useEffect} from 'react'
function HookCounterOne(){
const [count,setCount]=useState(0)
const [name,setName]=useState('')
useEffect(()=>{
console.log(`useEffect- updating document title`)
document.title=`you clicked ${count} times`
},[count])
return(
<div>
<input type="text" value={name} onChange={e=>setName(e.target.value)}/>
<button onClick={()=>setCount(count+1)} Click {count} times </button>
</div>
)
}
export default HookCounterOne
여기서 조건적으로 count value가 바뀔 때만 useEffect가 실행되게 어떻게 할 수 있을까?
=>useEffect에 [count]배열
을 넣어주자!
useEffect는 dependency를 명시해주지 않으면 항상 모든 렌더 후에 호출된다
import React, {useState,useEffect} from 'react'
function HookMouse(){
const [x,setX]= useState(0)
const [y,setY]= useState(0)
const logMousePosition =e=>{
console.log('mouse event')
setX(e.clientX)
setY(e.clientY)
}
useEffect(()=>{
console.log('useEffect called')
window.addEventListener('mousemove', logMousePosition)
},[])
return()
}
export default HookMouse
처음 render시에만 useEffect실행되고 그 다음부터는 re-render 안되게 어떻게 할 수 있을까?
useEffect 2번째 parameter에 빈배열[]
을 넣어주면 된다!
import React, {useState} from 'react'
import HookMouse from `./HookMouse`
function MouseContainer(){
const [display,setDisplay]= useState(true)
return(
<div>
<button onClick={()=>setDisplay(!display)}> Toggle display </button>
{display && <HookMouse/>}// toggle 기능
</div>
)
}
export default MouseContainer
이벤트 리스너를 제거하고 싶다면?
useEffect(()=>{
console.log(`useEffect called`)
window.addEventListener('mousemove',logMousePosition)
return ()=>{
console.log('Component unmounting code')
window.removeEventListener('mousemove',logMousePosition)
}
},[])
useEffect안에서 return
으로 removeEventListener를 작성하면 된다.
import React, {useState, useEffect} from 'react'
function IntervalHookCounter(){
const [count,setCount]=useState(0)
const tick=()=>{
setCount(prevCount=>prevCount+1)
}
useEffect(()=>{
function doSomething(){
console.log(someProp)
}
doSomething()
const interval=setInterval(tick,1000)
return()=>{
clearInterval(interval)
}
},[someProp])
return(
<div>
{count}
</div>
)
}
export default IntervalHookCounter
useEffect안에서 함수를 호출할 때, 함수를 useEffect안에서 작성해주자. dependency로 명시된 prop을 배열 안에 넣어준다.
빈 의존성 배열은 useEffect가 한번 실행된다는 것을 의미한다. 이 말은 즉 결과적으로 setInterval이 한번 생성된다는 것이다. 그럼 setInterval 콜백은 한번만 실행되기에 setCount(count+1)은 업데이트될 수 없다.
클로져로 인해 count는 항상 0이다. 그러면 timer는 항상 1을 보여줄 것이다. 그러므로 prevState=>prevState+1을 사용해서 count value 클로저 사용을 피해야 한다.
import React,{useState,useEffect} from 'react'
import axios from 'axios'
function DataFetching(){
const [posts,setPosts]=useState({})
const [id,setId] =useState(1)
const [idFromButtonClick,setIdFromButtonClick]=useState(1)
const handleClick=()=>{
setIdFromButtonClick(id)
}
useEffect(()=>{
axios
.get('https://jsonplaceholder.typicode.com/posts/${id}')
.then(res=>{
console.log(res)
setPosts(res.data)
})
.catch(err =>{
console.log(err)
})
},[idFromButtonClick])
의존성 배열[]을 넣어줘야 data fetch가 1번만 이루어진다.
idFromButtonClick을 배열 안에 넣어주면 버튼클릭할 때마다 fetching data가 되는 것이다
return (
<div>
<input type="text" value={id} onChange={e=>setId(e.target.value)}/>
<button type="button" onClick={handleClick}> Fetch Post </button>
<div> {post.title}</div>
// <ul>
//{ posts.map (post => <li key={post.id}> {post.title}</li>)
// </ul>
</div>
)
}
export default DataFetching