September 01, 2020
요즘 작성된 React코드를 보면 Hook을 이용한 코드들이 많다. 그래서 Class Component보다는 Function Component가 많이 사용되고 있다. 과연 Hook이란 무엇이고, Component는 어떤 것을 가로쳐길래 Hook이란 이름이 만들어 진 것 일까? 여기서 Hook에 대한 설명을 간단하게 정리해 보려고 한다.
Hook은 React 16.8부터 추가된 기능으로, Class를 작성할 필요없이 상태 값과 여러 React 기능을 사용하도록 하도록 만드는 기능이라고 볼 수 있다. 그렇다면 이런 기능은 무엇이 불편하기 때문에 나오게 되었을 까?
Component를 만들다 보면, Component 내부에서 사용되는 state나 lifecycle 함수를 중복되게 사용하는 경우가 있다. 이런 코드 재사용 성을 높이기 위해서 등장한 것이 HOC(High Order Component) 개념을 사용하는 것이다. HOC은 함수형 프로그래밍의 HOF 개념을 본따서 생긴 개념으로, Component를 입력으로 받고 새로운 Component를 반환하는 함수라고 보면 된다. 예를 들어 state를 재사용하기 위해서는 아래와 같은 방식으로 HOC를 생성하면 된다. 아래의 방식을 보면 알겠지만, 꽤나 복잡하다. 만약 좀 더 복잡한 Component를 하기 위해서는 여러개의 HOC를 적용했다고 생각해보자. 그럼 하나의 Component를 여러 개의 HOC로 감싸서 만들어야 한다. 그럼 callback hell처럼 wrapper hell이 발생한다.
Hook은 warpper를 사용하지 않고, 코드를 재사용할수 있도록 도와준다. 그래서 개발자들에게 좀 더 직관적으로 코드를 이해하면서 재사용 할 수 있도록 도와주는 기능을 담당한다.
# HOC
function withProps(InnerComp) {
return (
class OuterComp extends React.Component {
tmp = this.state
render() {
return <InnerComp {tmp} />
}
})
}React의 Component는 함수형 프로그래밍 지향하면서 만들어진 언어이다. 공식 홈페이지에도 다음과 같은 부분이 명시되어 있다.
All React components must act like pure functions with respect to their props.
그렇다면 우리는 왜 Function component가 아니라 Class component을 사용한단 말인가? 그건 컴포넌트의 상태를 관리하거나 Life Cycle 함수를 사용하기 위해서는 순수 함수로 구현이 어렵다. 그렇기 때문에 우리는 Class component를 사용하곤 했다. 하지만 React가 나오게 되면서 상태 관리나 Life cycle을 관리를 Function compoent에서 사용이 가능하게 만든다. 이는 컴포넌트를 함수형 프로그램으로 사용할 수 있다는 이야기다.
이렇게 class component에서 벗아나게 되면서 생기는 장점은 this와 같은 것을 사용하지 않고 구현이 가능하도록 도와 준다는 것이다. 이로써 React 개발자는 좀 더 코드를 잘 이해할 수 있도록 도와준다.
이처럼 hook을 사용하게 되면서 개발자는 손쉽게 개발을 할 수 있도록 도와주었다. 그럼 이번에는 여러 가지 Hook을 설명하고, 어떻게 Hook들이 사용되는 지 알아보도록 한다.
useState는 Function component에서 상태를 관리해주는 Hook이다. 사용방법은 매우 간단하다.
import React, { useState } from 'react'
const [value, setValue] = useState(0) // 입력 값으로 초기값을 넣어준다. 그럼 첫 값에는 state를 두 번째는 state을 입력하는 함수를 제공한다.useEffect는 Compoent가 렌더링 될 때마다 함수가 실행하도록 설정하는 Hook이다. 기존의 Class Component의 compoentDidMount와 compoentDidUpdate를 대체한 코드이다. 사용방법은 아래와 같습니다.
import React, { useEffect } from 'ract'
useEffect(() => {
console.log('aa')
}, [tmp])
// 입력 값으로 첫 번째는 실행될 함수를 입력하고, 두 번째에는 componentDidUpdate사용될 때처럼 update를 체크할 상태를 입력한다.Mount일 때만 사용
좀 전에 useEffect는 componentDidMount와 componentDidUpdate처럼 사용한다고 했다. 그럼 compoentDidMount처럼 사용하기 위해서는 어떻게 하는 지 알아보자.
import React, { useEffect } from 'ract'
useEffect(() => {
console.log('componentDidMount')
}, [])
// 두번째를 빈칸으로 둔다.두번째 입력 값을 빈칸으로 입력하면 Mount될 때만 Effect에 입력한 함수가 실행됩니다.
Update할 때 사용
특정 상태가 update될 때만 사용하기 위해서는 좀 전에 빈칸으로 둔 곳을 특정 상태 값을 넣어주면 됩니다.
import React, { useEffect } from 'ract'
useEffect(() => {
console.log('componentDidUpdate')
}, [name])
// 두번째에 특정 상태를 넣어준다.뒷정리
useEffect는 component가 UnMount되거나 Update 직전에 작업을 수행할 때에도 실행되도록 설정할 수 있다.
import React, { useEffect } from 'ract'
useEffect(() => {
console.log('componentDidMount')
return () => console.log('cleanup')
}, [])
// useEffect 첫 번째 함수의 return 에 cleanup 함수를 넣어준다.위 처럼 useEffect 첫 번째 함수의 return 에 cleanup 함수를 넣어주면 compoent unmount되거나 Update 직전에 cleanup 함수가 실행된다.
useContext는 Context 사용을 좀 더 편하게 도와주는 Hook입니다. Context는 부모에서 자식 컴포넌트에 순차적으로 props을 전달해야 되는 데, context를 사용하면 순차적으로 props을 전달하지 않고, 여러 컴포넌트에 props을 전달해시켜주는 기능입니다. 아래 코드는 기존 Context의 사용 방법을 간단하게 적어 본 것 이다.
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
return (
<ThemeContext.Provider value='dark'>
<ThemedButtion />
</ThemeContext.Provider>
)
}
}
class ThemeButton extends React.Component {
static contextType = ThemeContext;
render () {
return <Button theme={this.context}>
}
}이런 이런 react context를 어떻게 hook으로 사용하는 방법을 알아보려고 한다.
import { useContext, createContext } from 'react'
const ThemeContext = createContext('light')
const ContextSample = () => {
const theme = useContext(ThemeContext)
return <Button theme={theme} />
}useReducer를 이용하면 Redux를 좀 더 쉽게 사용할 수 있도록 도와준다. 예시 코드 들어서 어떻게 사용되는 지 알아본다.
import React, { useReducer } from 'react'
function reducer(state, action) {
switch (action.type) {
case 'INCREASE':
return { value: state.value + 1 }
}
}
const Counter = () => {
const [state, dispatch] = useReducer(reducer, { value: 0 })
// 입력값으로 reducer함수, 초기 state를 넣어준다.
// 반환값으로 state 값과 dispatch 함수를 반환다.
return (
<div>
<p>state.value</p>
<button onClick={() => dispatch({ type: 'INCREASE' })} />
</div>
)
}useMemo는 연산을 최적화 할 때 사용하는 Hook이다. Component를 렌더링할 때, 특정 값이 변화 되었을 때만 렌더링되고, 변화가 없을 경우에는 렌더링이 적용되지 않도록 도와주는 Hook이다. 사용 방법은 아래와 같다.
import React, { useMemo } from 'react'
function Component({ x, y }) {
const z = useMemo(() => component(x, y), [x, y]) // 첫번째 값으로 함수를 넣고, 두 번째에는 변화를 확인할 값을 입력한다. 그리고 결과 값으로 함수의 결과 값이 반환된다.
return <div>{z}</div>
}하지만 이를 사용한다고 해서 좋은 것만은 아니다. 보통은 시간이 많이 걸리는 작업은 useEffect를 통해서 비동기로 처리가 되고, 이를 사용하면 useMemo가 적용된 값은 Garbage collection에서 제한되기 때문에 메모리를 더 쓰게 됩니다. 그러니 상황을 봐서 사용하는 게 좋아보인다.
useCallback도 useMemo와 같이 이벤트 핸들러와 관련해서 성능을 최적화할 때 사용되는 Hook 입니다. 보통 이벤트 핸들러는 컴포넌트가 리렌더링될 때마다 새로 생성됩니다. 하지만 useCallback을 사용하면 특정값이 변화될 때에만 수행됩니다. 사용 방법은 아래와 같습니다.
import React, { useCallback } from 'react'
function Component() {
const onChange = useCallback((e) => setValue(e.target.value), []) // 첫번째 값으로 이벤트 핸들러를 넣고, 두 번째는 변화를 확인할 값을 입력하고, 반환 값으로 이벤트 핸드러를 반환한다.
// []을 입력하면 Component가 Mount될 때만 실행됩니다.
}이 useCallback은 useMemo를 이용해서 만들어 진 것이다. 위 코드를 useMemo를 사용하면 아래와 같습니다.
const onChange = useMemo(() => {
const fn = (e) => {
setValue(e.target.value)
}
return fn
}, [])useRef는 ref를 쉽게 사용할 수 있는 Hook입니다. 실제 해당 DOM을 지정할 때 사용합니다. useRef의 반환값을 특정 element의 ref 값으로 전달하게 되면, ref의 current값이 해당 DOM을 가르키게 됩니다.
import React, { useRef } from 'react'
function Component() {
const inputEl = useRef('') // ref의 current를 초기값을 입력한다.
const onInsert = e => {inputEl.current.focus()}
return (
<input ref={inputEl} />
<button onClick={onInsert}>
)
}또한, 렌더링과 상관없는 변수 값을 지정할 때에도 useRef를 사용할 수 있습니다. 이렇게 사용하게 되면 ref값이 바뀌어도 컴포넌트가 렌더링이 되지 않습니다.
import React, { useRef } from 'react'
function Component() {
const id = useRef(1)
const printId = () => {
console.log(id.current)
}
return <div></div>
}Hook은 자신이 직접 만들어 볼수 있다. 이를 통해서 자주 사용되면 hook기능을 직접만들어서 코드를 좀 더 효율적으로 작성할 수 있다.
https://ko.reactjs.org/docs/hooks-intro.html https://ui.toast.com/weekly-pick/ko_20171117/ https://velog.io/@velopert/react-hooks https://www.daleseo.com/react-hooks-use-memo/ https://react.vlpt.us/basic/10-useRef.html