본문 바로가기

오픈소스/노드

[Node] React 정리(16) - 커스텀 Hooks 만들기

 

리액트로 프로젝트를 생성하여 개발하는 과정 중 리액트의 필수 문법 및 기초적인 부분, 심화 과정 등을 정리한 문서입니다. 정리한 부분에는 제가 이해하는 관점에서만 정리를 하였기 때문에 초점은 보여주는 형식이 아닌 제가 필요할 때 사용하려는 목적이 담겨져 있습니다. 컴포넌트를 만들다보면, 반복되는 로직이 자주 발생합니다. 예를 들어서 input 을 관리하는 코드는 관리 할 때마다 꽤나 비슷한 코드가 반복되죠. 이번에는 그러한 상황에 커스텀 Hooks 를 만들어서 반복되는 로직을 쉽게 재사용하는 방법을 알아보겠습니다.

 

 현재 정리부터는 소스코드를 제공할 예정입니다. 제공하는 소스코드를 이용하여 따라하실 수 있고 개념을 확실히 잡을 꺼 같아서 올리게 되었습니다. 소스코드 자료는 아래 링크를 참고해주세요.

https://github.com/libtv/React_Note/tree/master/16.%20customHook

 

GitHub - libtv/React_Note: This is a notebook organized by React.

This is a notebook organized by React. Contribute to libtv/React_Note development by creating an account on GitHub.

github.com

 

러면 custom Hook 을 만들어봅시다.

 

 

# Contents


  • custom Hooks 만들기

 

 

# custom Hooks 만들기


 커스텀 훅을 만드는 방법은 아주 간단합니다. 그냥, 그 안에서 useState, useEffect, useReducer, useCallback 등 Hooks 를 사용하여 원하는 기능을 구현해주고, 컴포넌트에서 사용하고 싶은 값들을 반환해주면 됩니다.

 그러면 본격적으로 우리가 사용하는 예제를 통해 커스텀 훅을 사용해보고, 만들어보는 시간을 갖도록 하겠습니다.

 

 먼저 src 디렉터리에 hooks 라는 디렉터리를 만들고, 그 안에 useInputs.js 라는 파일을 만드세요. 훅을 만들 때 사용하는 규칙이 있는데, 보통 어떠한 훅 전에 use 라는 접두사를 붙이게 됩니다. 예를 들어 useState, useEffect, useReducer 등등 처럼 말이죠.

 

 그러면 본격적으로 아래 내용을 통해 useInput.js 를 코딩하겠습니다. useInput.js

 

import { useState, useCallback } from "react";

function useInput(initialForm) {
    const [inputs, setInputs] = useState(initialForm);
    const onChange = useCallback((e) => {
        var { name, value } = e.target;
        setInputs({
            ...inputs,
            [name]: value,
        });
    });

    const reset = useCallback(() => {
        setInputs(initialForm);
    });

    return [inputs, onChange, reset];
}

export default useInput;

 

 useState를 통해 form 데이터를 가져올 것입니다. 그리고, onChange 메소드와 reset 메소드를 생성하여 외부의 여러 파일에서도 사용할 수 있게 하였습니다.

 

 다음은 App.js 코드입니다.

 

import "./App.css";
import react, { useCallback, useReducer, useRef } from "react";
import UserList from "./UserList.js";
import CreateUser from "./CreateUser";
import useInput from "./hooks/useInput";

function App() {
    const nextIndex = useRef(4);

    function reducer(state, action) {
        switch (action.type) {
            case "CREATE_USER":
                return {
                    users: state.users.concat(action.user),
                };
            case "TOGGLE_USER":
                return {
                    users: state.users.map((user) => {
                        return user.id === action.userId ? { ...user, active: true } : user;
                    }),
                };
            case "DELETE_USER":
                return {
                    users: state.users.filter((user) => {
                        return user.id !== action.userId;
                    }),
                };
            default:
                break;
        }
        return state;
    }

    const initForm = { name: "", email: "" };
    const [form, onChange, reset] = useInput(initForm);

    const initialState = {
        users: [
            { id: 0, name: "John", email: "john@gmail.com", active: true },
            { id: 1, name: "Smith", email: "smith@gmail.com", active: false },
            { id: 2, name: "Park", email: "park@gmail.com", active: false },
            { id: 3, name: "Kim", email: "kim@gmail.com", active: false },
        ],
    };

    const [state, dispatch] = useReducer(reducer, initialState);
    const { users } = state;
    const { name, email } = form;

    const onClick = useCallback(() => {
        dispatch({
            type: "CREATE_USER",
            user: {
                id: nextIndex.current++,
                name,
                email,
                active: false,
            },
        });
        reset();
    }, [users, name, email]);

    const onToggle = useCallback(
        (userId) => {
            dispatch({
                type: "TOGGLE_USER",
                userId,
                form,
            });
        },
        [users]
    );

    const onDelete = useCallback(
        (userId) => {
            dispatch({
                type: "DELETE_USER",
                userId,
                form,
            });
        },
        [users]
    );

    return (
        <div>
            <CreateUser name={name} email={email} onChange={onChange} onClick={onClick} />
            <UserList users={users} onToggle={onToggle} onDelete={onDelete} />
            <div>활성화된 사용자 수 : 0</div>
        </div>
    );
}

export default App;

 

우리가 만든 useInput 이라는 Hook을 불러와서 setInput 메소드가 있었던 자리에 넣고, 이것 저것 수정한 결과입니다.

이렇게 커스텀 Hook 을 만들어서 사용하면 컴포넌트의 로직을 분리시켜서 필요 할 때 쉽게 재사용 할 수도 있습니다.