Front-end/React

[React] Custom Hook 만들기

냥인 2021. 6. 7. 18:54
728x90
반응형

Custom Hook 만들기

컴포넌트를 만들다 보면 반복되는 로직이 발생합니다.

예를 들면 인풋 값을 바꾸어주는 로직의 경우, 항상 event.target에서 name과 value를 받아와서 그 값을 변경해준다는 로직이 같습니다.

이렇게 반복되는 로직은 기존의 Hook들을 응용해서 커스텀 Hook을 만들 수 있습니다.

 

인풋 상태를 관리하는 커스텀 Hook 만들어보기

import React, { useCallback, useState } from "react";

const useInputs = (initialForm) => {
  const [form, setForm] = useState(initialForm);
  const onChange = useCallback((event) => {
    const { name, value } = event.target;
    setForm((form) => ({ ...form, [name]: value }));
  }, []);
  const reset = useCallback(() => {
    setForm(initialForm);
  }, [initialForm]);

  return [form, onChange, reset];
};

export default useInputs;

UseInputs라는 컴포넌트를 만들었습니다.

이 컴포넌트는 초기의 initialForm이라는 것을 받아와서 그것을 useState의 초기값으로 설정합니다.

그리고 onChange와 reset 함수를 위와 같이 작성합니다.

마지막으로 form, onChange, reset을 배열 형태로 리턴하면 됩니다. (객체 형태로 리턴해도 상관 없습니다)

 

만든 useInputs 사용해보기

import React, { useReducer, useRef, useMemo, useCallback } from "react";
import "./App.css";
import CreateUser from "./CreateUser";
import useInputs from "./useInputs";
import UserList from "./UserList";

function countActiveUsers(users) {
  return users.filter((user) => user.active).length;
}
const initialState = {
  users: [
    {
      id: 1,
      username: "velopert",
      email: "public.velopert@gmail.com",
      active: false,
    },
    {
      id: 2,
      username: "tester",
      email: "tester@example.com",
      active: false,
    },
    {
      id: 3,
      username: "liz",
      email: "liz@example.com",
      active: false,
    },
  ],
};
const reducer = (state, action) => {
  switch (action.type) {
    case "CREATE_USER":
      return {
        users: state.users.concat(action.user),
      };
    case "REMOVE_USER":
      return {
        ...state,
        users: state.users.filter((user) => user.id !== action.id),
      };
    case "TOGGLE_USER":
      return {
        ...state,
        users: state.users.map((user) =>
          user.id === action.id ? { ...user, active: !user.active } : user
        ),
      };
    default:
      throw new Error("Unhandled action");
  }
};
function App() {
  const [state, dispatch] = useReducer(reducer, initialState);
  const [form, onChange, reset] = useInputs({
    username: "",
    email: "",
  });
  const { username, email } = form;
  const nextId = useRef(4);
  const { users } = state;

  const onCreate = useCallback(() => {
    dispatch({
      type: "CREATE_USER",
      user: {
        id: nextId.current,
        username,
        email,
      },
    });
    reset();
    nextId.current += 1;
  }, [username, email, reset]);

  const onRemove = useCallback((id) => {
    dispatch({
      type: "REMOVE_USER",
      id,
    });
  }, []);

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

  const count = useMemo(() => countActiveUsers(users), [users]);

  return (
    <>
      <CreateUser
        username={username}
        email={email}
        onChange={onChange}
        onCreate={onCreate}
      />
      <UserList users={users} onRemove={onRemove} onToggle={onToggle} />
      <div>활성 사용자 수 : {count} </div>
    </>
  );
}

export default App;

이제 useInputs를 이용해서 username과 email을 관리하면 초기 initialState에서도 inputs가 필요 없어지므로 지우고

onCreate 함수(등록 버튼을 눌러서 사용자가 추가되는 함수)에서 input에서 username과 email도 초기화가 되어야 하므로 그것은 useInputs에서 만든 reset 함수를 실행합니다.

그리고 onChange는 useInputs에서 만들어온 것이므로 바로 CreateUser에 props로 전달해줄 수 있습니다.

728x90
반응형