Skip to content

Instantly share code, notes, and snippets.

@ninanung
Created August 29, 2019 08:40
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ninanung/cb199ad80ac29da4ca6111b970956d79 to your computer and use it in GitHub Desktop.
Save ninanung/cb199ad80ac29da4ca6111b970956d79 to your computer and use it in GitHub Desktop.
때늦은 React Hooks 시리즈 5탄 - useReducer/Custom Hooks

이 글의 코드는 저자의 Github에서 확인할 수 있습니다. 이 글은 Redux를 직접 사용해 보았거나 최소한 관련 지식이 있다는 전제하에 설명합니다. 만약 모른다면 관련 공부를 먼저 해주세요.

때늦은 React Hooks 시리즈 5탄 - useReducer/Custom Hooks

저번글에서 React Hooks를 이용한 router를 설명해 보겠다고 했으나 계획을 조금 변경했습니다. 아무래도 이 부분은 설명하고 넘어가야 어떻게 다른 Hooks들이 동작하는지를 설명할 수 있을 것 같았습니다. 우선은 미뤄뒀던 useReducer를 설명해보죠.

useReducer

Redux, Reducer에 대한 개념은 React의 다양한 컴포넌트들에서 통합적으로 state를 관리하기 위해 고안되었습니다. 이 말이 무엇이냐면, 어플리케이션 전체를 관통하는 상태관리가 필요했기 때문에 만들어 졌다는 말입니다. 컴포넌트들의 상하에 따라 값을 주고 받는 것에 한계가 있고, 어떻게 구현을 한다고 해도 관리가 복잡해지는 문제가 있었기 때문입니다. useReducer는 함수형 컴포넌트에서의 Redux를 어느정도 대신하는 역할을 합니다. 어차피 설명을 줄줄이 하는 것 보다는 직접 보는게 빠르기 때문에 코드를 우선 보도록 하죠.

import React, {useReducer} from 'react';

const reducer = (state, action) => {
    switch(action.type) {
        case 'CHANGE':
            return action.value;
        default:
            return state;
    }
}

const UseReducer = () => {
    const [state, dispatch] = useReducer(reducer, 'initial state');

    return (
        <div>
            <h3>{state}</h3>
            <input type='text' value={state} onChange={(e) => dispatch({value: e.target.value, type: 'CHANGE'})} />
        </div>
    )
}

export default UseReducer;

위의 코드는 지금까지 여러번 구현했던 "input안의 텍스트가 바뀌면 state를 변경하는 기능"의 코드입니다. 이젠 딱 보면 느낌이 올 거라고 생각합니다. reducer함수는 기존의 Redux가 그러했던 것 처럼 action의 type에 따라서 어떻게 state를 변경할 건지를 switch문으로 골라내도록 구현했습니다. Redux를 자주 사용했다면 상당히 익숙한 코드일 것이라고 생각합니다. 위의 코드에서

const [state, dispatch] = useReducer(reducer, 'initial state');

이부분이 가장 주요한 포인트인데, useReducer를 사용하여 state와 해당state의 값을 변경할 dispatch를 선언하고 있습니다. useReducer함수의 첫번째 인자는 처음에 만들어 놓은 reducer함수를 넘기고, 두번째 인자에는 state의 최초값을 넘기면 됩니다. 여기까지 하면 사실상 모든 준비는 끝나고 사용하기만 하면 됩니다.

<input type='text' value={state} onChange={(e) => dispatch({value: e.target.value, type: 'CHANGE'})} />

input에서 value는 state자체를 줘서 input안의 값을 유지하고, onChangedispatch를 사용하는 함수를 넘겨줬죠. 이렇게 하면 input안의 값이 변경될 때 마다 state값이 변경되고 변경된 값이 다시 input에 반영되게 됩니다. 참고로 여기서 dispatch를 통해 넘겨준 type이나 value는 필수값이 아닙니다. Redux를 사용했던 사람들은 뭔가 type을 의무적으로 넘겨줘야 할 것 같지만, 전혀 그렇지 않습니다. dispatch('test')와 같이 문자열을 넘겨줘도 되고 숫자나 배열등 필요한 것을 넘겨주면 그만입니다. 간단하죠?

커뮤니티 상에서는 useReducer가 Redux를 완전히 대신할 수 있는가에 대한 논의가 아직 진행중인 것으로 보입니다. 이에 대해서는 스스로 판단해야 할 것 같습니다.

Custom Hooks

React Hooks는 몇가지 기본 hook들을 지원하고 있고 그것들도 충분히 유용하지만, 자기만의 hook을 만들어서 사용하기를 권장합니다. 작년애 React Hooks가 발표된 이후로 많은 연구와 스터디가 진행되었고, 정말로 많은 hook들이 세상에 나왔죠. 아마 "이런 hook이 있으면 좋겠는걸?"이라고 생각하는 기능이 있다면, 아마 대부분은 이미 있을 겁니다. 참 개발자라는 족속들은 이런걸 좋아하거든요. 어차피 각자가 원하는 기능들은 다 다르기 때문에 이 지면을 통해 그런 hook들을 일일이 설명하는 것은 큰 의미가 없을 것 같고요, 어떻게 커스텀 훅을 만들면 좋을지 설명해 보겠습니다. 사실 원리 자체는 정말 간단합니다. 기존의 hook이나 기능들을 사용하여 새로운 hook을 만드는 겁니다. 우선 코드를 보실까요.

import React, {useReducer} from 'react';

const reducer = (state, action) => {
    switch(action.type) {
        case 'CHANGE':
            return action.value;
        default:
            return state;
    }
}

const useInput = (type, initialState) => {
    const [state, dispatch] = useReducer(reducer, initialState);
    const onChange = (e) => {
        dispatch({type, value: e.target.value})
    }

    return [state, onChange]
}

const CustomHooks = () => {
    const [state, onChange] = useInput('CHANGE', 'initial state');

    return (
        <div>
            <h1>Custom Hooks</h1>
            <h3>{state}</h3>
            <input type='text' value={state} onChange={onChange} />
        </div>
    )
}

export default CustomHooks;

자, 이 코드는 그냥 useReducer를 설명할 때 사용했던 것을 조금 변경한 것 뿐입니다. useReducer훅을 직접 컴포넌트에서 사용했던 것을 재사용이 가능하도록 변경했을 뿐이죠. 이름은 useInput이라고 지었습니다. 해당 hook안에서는 useReducer hook을 사용하며, 변경된 state와 input안에서 state를 변경할 onChange함수를 같이 선언할 수 있습니다. useState와 비슷하게 사용할 수 있다고 보면 되겠습니다. 물론, 만들어진 hook이 꼭 이런 형태일 필요가 있는 건 아닙니다. useEffect와 같이 렌더링시 특정 기능을 하는 hook일수도 있고, 만드는 방법에 따라 달라지겠죠. 개인적으로는 이미 깃허브나 여타 블로그에 올라와있는 Custom Hooks들 코드를 보고 이렇게 만들면 되겠구나 라는걸 자세하게 파악하시길 권장합니다.

알고 계시겠지만, 이 코드는 정말로 간단한 하나의 예제를 보여드린 것 뿐입니다. 아마 원하는 기능에 따라 얼마든지 복잡해 질 수 있고, 혹은 다른 기능을 사용해야 할 수도 있죠. React Hooks에 대해 설명할 때 가끔 드렸던 말씀이지만, 결국은 어떻게 사용하느냐가 관건입니다. 이건 어디에 저건 어디에 써야겠다는 생각을 하면서 설명을 읽었다면 금방 유용하게 사용할 수 있을 겁니다. React Hooks에 대한 설명은 여기까지가 끝입니다. 때는 늦었지만, 충분히 유용하게 사용할 수 있을 것 같습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment