yarn create react-app hooks-tutorial
useState
함수 컴포넌트에서 상태 관리
import React, {useState} from 'react'
const [value, setValue] = useState(0)
useEffect
리액트 컴포넌트가 렌더링될 때마다 특정 작업을 수행하도록 설정할 수 있는 Hook
useEffect(() => {
console.log({name, nickname})
})
useEffect(() => {
console.log('마운트될때 실행')
}, [])
배열 안에 useState를 통해 관리하고 있는 상태를 넣어도 되고, props로 전달받은 값을 넣어도 된다
useEffect(() => {
console.log(name)
}, [name])
컴포넌트가 언마운트되기 전이나 업데이트되기 직전에 어떠한 작업을 수행하고 싶다면 cleanup 함수를 반환해줘야 한다.
useEffect(() => {
console.log(name)
return () => {
console.log('cleanup')
}
}, [name])
빈 배열을 넣는다.
useEffect(() => {
console.log(name)
return () => {
console.log('cleanup')
}
}, [])
useReducer
useState보다 더 다양한 컴포넌트 상황에 따라 다양한 상태를 다른 값으로 업데이트해 주고 싶을 때 사용하는 Hook
function reducer(state, action) {
return { ... }
}
// 액션값
{ type: 'INCREMENT', ... }
useReducer(reducer 함수, 해당 reducer의 기본값)
e.target 값을 액션값으로 사용하면 input 개수가 많아져도 코드를 깔끔하게 유지할 수 있다.
import React, {useReducer} from 'react';
function reducer(state, action) {
return {
...state,
[action.name]: action.value
}
}
const Info = () => {
const [state, dispatch] = useReducer(reducer, {
name: '',
nickname: ''
})
const {name, nickname} = state
// e.target 값 자체를 액션 값으로 사용
const onChange = e => {
dispatch(e.target)
}
return (
<div>
<div>
<input name="name" value={name} onChange={onChange}></input>
<input name="nickName" value={nickname} onChange={onChange}></input>
</div>
<div>
<div>이름: {name}</div>
<div>닉네임: {nickname}</div>
</div>
</div>
)
}
export default Info
useMemo
import {useState, useMemo} from 'react'
const getAverage = numbers => {
if(numbers.length === 0) return 0
const sum = numbers.reduce((a,b) => a+b)
return sum/numbers.length
}
const Average = () => {
const [list, setList] = useState([])
const [number, setNumber] = useState('')
const onChange = e => {
setNumber(e.target.value)
}
const onInsert = () => {
const nextList = list.concat(parseInt(number))
setList(nextList)
setNumber('')
}
// list 배열의 내용이 바뀔때만 getAverage 함수 호출된다
const avg = useMemo(() => getAverage(list), [list])
return (
<div>
<input value={number} onChange={onChange}></input>
<button onClick={onInsert}>등록</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>평균값 {avg}</div>
</div>
)
}
export default Average
useCallback
useCallback(생성하고 싶은 함수, [해당 값이 바뀌었을 때 함수를 새로 생성])
import {useState, useMemo, useCallback} from 'react'
const getAverage = numbers => {
if(numbers.length === 0) return 0
const sum = numbers.reduce((a,b) => a+b)
return sum/numbers.length
}
const Average = () => {
const [list, setList] = useState([])
const [number, setNumber] = useState('')
const onChange = useCallback(e => {
setNumber(e.target.value)
}, []) // 컴포넌트가 처음 렌더링될 때만 함수 생성
const onInsert = useCallback(() => {
const nextList = list.concat(parseInt(number))
setList(nextList)
setNumber('')
}, [number, list]) // number 또는 list가 바뀌었을 때만 함수 생성
// list 배열의 내용이 바뀔때만 getAverage 함수 호출된다
const avg = useMemo(() => getAverage(list), [list])
return (
<div>
<input value={number} onChange={onChange}></input>
<button onClick={onInsert}>등록</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>평균값 {avg}</div>
</div>
)
}
export default Average
useRef
ref를 설정하면 객체 안의 current 값이 실제 엘리먼트를 가리킨다.
import {useState, useMemo, useCallback, useRef} from 'react'
const getAverage = numbers => {
if(numbers.length === 0) return 0
const sum = numbers.reduce((a,b) => a+b)
return sum/numbers.length
}
const Average = () => {
const [list, setList] = useState([])
const [number, setNumber] = useState('')
const inputEl = useRef(null)
const onChange = useCallback(e => {
setNumber(e.target.value)
}, []) // 컴포넌트가 처음 렌더링될 때만 함수 생성
const onInsert = useCallback(() => {
const nextList = list.concat(parseInt(number))
setList(nextList)
setNumber('')
inputEl.current.focus()
}, [number, list]) // number 또는 list가 바뀌었을 때만 함수 생성
// list 배열의 내용이 바뀔때만 getAverage 함수 호출된다
const avg = useMemo(() => getAverage(list), [list])
return (
<div>
<input value={number} onChange={onChange} ref={inputEl}></input>
<button onClick={onInsert}>등록</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>평균값 {avg}</div>
</div>
)
}
export default Average
로컬 변수 = 렌더링과 상관없이 바뀔 수 있는 값
커스텀 Hooks 만들기
useInputs.js
import React, {useReducer} from 'react';
function reducer(state, action) {
return {
...state,
[action.name]: action.value
}
}
export default function useInputs(initialForm) {
const [state, dispatch] = useReducer(reducer, initialForm)
// e.target 값 자체를 액션 값으로 사용
const onChange = e => {
dispatch(e.target)
}
return [state, onChange]
}
Info.js
import useInputs from './useInputs';
const Info = () => {
const [state, onChange] = useInputs({
name: '',
nickname: ''
})
const {name, nickname} = state
return (
<div>
<div>
<input name="name" value={name} onChange={onChange}></input>
<input name="nickName" value={nickname} onChange={onChange}></input>
</div>
<div>
<div>이름: {name}</div>
<div>닉네임: {nickname}</div>
</div>
</div>
)
}
export default Info