리액트의 함수형 컴포넌트에서 사용하는 Hooks을 정리해보자
Hook은 함수형 컴포넌트에서 리액트 state와 생명주기 기능(lifecycle features)을 "연동(hook into)"할 수 있게 해주는 함수이다.
함수형 컴포넌트에서 사용하는 state를 변화할 수 있도록 제공되는 훅이다. 리액트에서 state를 직접 변경하는 것은 불가하기 때문에 클래스형 컴포넌트에서도 setState가 제공되는 것처럼 함수형 컴포넌트에도 있다.
useState를 사용해서 상태를 변화시킨 경우 해당 컴포넌트가 다시 렌더링된다.
useState는 상태변수와, 그 상태값을 변화시킬 수 있는 함수가 반환된다.
import React, { useState } from 'react'
const App = () => {
const [name, setName] = useState('');
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { value } = e.target.value;
setName(() => value);
}
return (
<>
<input type="text" name="name" onChange={handleChange} value={name}/>
</>
);
}
export default App
리액트 함수형 컴포넌트가 실행해서 렌더링될 때 컴포넌트에 작용하는 effect함수이다.
클래스형 컴포넌트에서 수행하던 Life-Cycle 중 componentDidMount
, componentDidUpdate
, componentWillUnmount
를 통합하여 사용할 수 있는 Hook이다.
나 ,
컴포넌트가 렌더링된 후 useEffect에 전달된 콜백함수가 실행된다.
두번째 인자에 특정 값이나 함수를 배열로 전달하여 dependencies
에 맞게 선택적으로 effect 콜백함수를 실행할 수 있다.
import React, { useState, useEffect } from 'react'
const App = () => {
const [name, setName] = useState('');
useEffect(() => {
console.log('useEffect 실행1');
return () => {
console.log('useEffect clean up');
}
});
useEffect(() => {
console.log('useEffect 실행2');
}, []);
useEffect(() => {
console.log('useEffect 실행3');
}, [name]);
return (
<>
<input type="text" name="name" onChange={handleChange} value={name}/>
</>
);
}
export default App
useEffect 콜백함수에서 clean up 함수를 반환하여 해당 컴포넌트가 unmount될 때 클린업함수를 실행해서 해당 컴포넌트에서 사용하는 외부라이브러리나 모듈 등과 같이 리소스를 정리할 수 있다.
React.createContext
와 함께 사용되는 훅이다. 리액트는 단방향 데이터 flow를 갖는데, props drilling이라고도 한다. 상위 컴포넌트 -> 하위 컴포넌트로 props를 전달하는 형태로 데이터를 전달한다.
이러한 특성은 컴포넌트가 많이 중첩된 경우 props를 전달하기 위해 불필요하게 중간에 거치는 컴포넌트가 많아질 수 있다는 것이다.
import React, { createContext, useContext } from 'react'
const themes = {
light: {
background: '#fff'
},
dark: {
background: '#222'
}
}
const ThemeContext = createContext(themes.light)
const App = () => {
return (
<>
<ThemeContext.Provider value={themes.dark} >
<Child />
</ThemeContext.Provider>
</>
);
}
const Child = () => {
const theme = useContext(ThemeContext);
return (
<>
<div style={{ background: theme.background }}>
this is child
</div>
</>
);
}
컴포넌트의 중첩이 얼마가 되든 useContext(컨텍스트 객체);
를 사용하면 가장 가까운 context provider가 사용된다.
const refContainer = useRef(initialValue);
useRef는 initialValue
로 초기화된 ref객체를 반환한다. 이 ref객체는 변경가능하며 .current
프로퍼티로 접근할 수 있다.
import React, { useRef } from 'react'
const InputWithFocusButton = () => {
const inputEl = useRef(null);
const handleClick = () => {
inputEl.current.focus();
}
return (
<>
<input type="text" ref={inputEl} />
<button onClick={handleClick}>버튼에 포커스</button>
</>
);
}
inputEl
은 null로 초기화된 컨테이너이고 렌더링 시 input Element를 담았다.
버튼 클릭할 때 inputEl.current
로 input DOM에 접근하여 focus API를 호출했다.
useState의 대체함수이다.
const [state, dispatch] = useReducer(reducer, initialState);
reducer는 (state, action) => newState
형태를 갖는 함수이다.
다수의 하윗값을 포함하는 복잡한 정적 로직을 만드는 경우나 다음 state가 이전 state에 의존적인 경우에 보통 useState보다 useReducer를 선호한다.
import React, { useReducer } from 'react'
const initialState = { count: 0 }
const reducer = (state, action) => {
switch(action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
const Counter = () => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state}
<button onClick={() => dispatch({ type: 'increment' })}> + </button>
<button onClick={() => dispatch({ type: 'decrement' })}> - </button>
</>
);
}
의존배열을 기준으로 메모이제이션된 콜백함수를 반환한다.
const memoCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
리액트 함수형 컴포넌트가 렌더링될 때 내부에 선언한 함수는 기능이 동일하더라도 그 함수의 참조값이 변경돼서 새로운 참조값(주소값)이 할당된다. 그래서 useCallback을 사용한 내부함수를 자식 컴포넌트에게 props로 전달하여 컴포넌트 렌더링 성능최적화를 할 수 있다.
의존배열을 기준으로 메모이제이션된 값을 반환한다.
const memoValue = useMemo(() => doSomething(a, b), [a, b]);
useCallback과 마찬가지로 의존배열에 있는 값이 변경될 때만 useMemo 콜백함수를 재실행하여 새로운 값을 반환한다.
useCallback
과useMemo
로 사용된 함수와 값을 자식 컴포넌트에게 전달한 경우, 자식 컴포넌트를React.memo
로 사용하여 감싸면 memoized component가 되어 렌더링 성능최적화를 할 수 있다.