React

React - 커스텀 hook 만들기

Starters 2020. 9. 13. 19:18

1. useInputs.js 생성하고 input에 관한 함수 만들기

import { useState, useCallback } from 'react';

// initialForm 은 input form에서 관리할 초기값
function useInputs(initialForm) {
  // form이라는 새로운 상태 선언 - 그 상태의 초기값은 파라미터로 가져온 initialForm
  const [form, setForm] = useState(initialForm);
  const onChange = useCallback(e => {
    const {name, value} = e.target;
    // form을 업데이트
    setForm(form => ({ ...form, [name]: value}));
    // 의존하는 다른 상태 없음 빈 - 배열 사용
  }, []);
  // 초기값을로 설정해주겠다는 뜻
  const reset = useCallback(() => setForm(initialForm), [initialForm]);

  // 이제 useInputs사용하면 초기값을 파라미터로 받아오고,
  // onChange로 상태 관리
  // reset으로 초기화
  return [form, onChange, reset];
}

export default useInputs;

 

2. App.js 에 적용

1) initState 객체에서 inputs 부분 삭제

2) useInputs 컴포넌트 선언

import useInputs from './useInputs';

3) { username, email } 선언 부분, onChange()부분 삭제

4) useInput()선언하고, username과 email form에서 추출

const [form, onChange, reset] = useInputs({
    username: '',
    email: '',
  });
  const { username, email } = form;

5) reset은 onCreate할 때, 추출

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

결과 코드

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

function countActiveUsers(users){
  console.log('활성 사용자 수를 세는 중...');
  return users.filter(user => user.active).length;
}

const initialState = {
  users: [
    {
      id: 1,
      username: 'starters',
      email: 'public.starters@gmail.com',
      active: true,
    },
    {
      id: 2,
      username: 'tester',
      email: 'tester@gmail.com',
      active: false,
    },
    {
      id: 3,
      username: 'jajajojo',
      email: 'jajajojo@gmail.com',
      active: false,
    }
  ]
}

function reducer(state, action){
  switch (action.type) {
    case 'CHANGE_INPUT':
      return {
        ...state,
        inputs: {
          ...state.inputs,
          [action.name]: action.value
        }
      };
    case 'CREATE_USER':
      return {
        inputs: initialState.inputs,
        users: state.users.concat(action.user)
      };
    case 'TOGGLE_USER':
      return {
        ...state,
        users: state.users.map(user =>
          user.id === action.id
            ? { ...user, active: !user.active }
            : user
        )
      };
    case 'REMOVE_USER':
      return {
        ...state,
        users: state.users.filter(user => user.id !== action.id)
      };
    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,
      }
    });
    nextId.current += 1;
    reset();
  }, [username, email, reset]);

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

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

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

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

export default App;

 

useState 대신 useReducer 사용하는 방법

import { useReducer, useCallback } from 'react';

function reducer(state, action){
  switch (action.type) {
    case 'CHANGE':
      return {
        ...state,
        [action.name]: action.value
      }
    case 'RESET':
      return Object.keys(state).reduce((acc, current) => {
        acc[current] = '';
        return acc;
      }, {});
    default:
      return state;
  }
}

function useInputs(initialForm) {
  const [form, dispatch] = useReducer(reducer, initialForm);
  const onChange = useCallback(e => {
    const {name, value} = e.target;
    dispatch({
      type: 'CHANGE',
      name,
      value
    });
  }, []);
  const reset = useCallback(() => {
    dispatch({
      type: 'REMOVE',
    })
  }, []);

  return [form, onChange, reset];
}

export default useInputs;

 

 

이전 코드

import React, { useRef, useReducer, useMemo, useCallback } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';

function countActiveUsers(users){
  console.log('활성 사용자 수를 세는 중...');
  return users.filter(user => user.active).length;
}

const initialState = {
  inputs: {
    username: '',
    email: '',
  },
  users: [
    {
      id: 1,
      username: 'starters',
      email: 'public.starters@gmail.com',
      active: true,
    },
    {
      id: 2,
      username: 'tester',
      email: 'tester@gmail.com',
      active: false,
    },
    {
      id: 3,
      username: 'jajajojo',
      email: 'jajajojo@gmail.com',
      active: false,
    }
  ]
}

function reducer(state, action){
  switch (action.type) {
    case 'CHANGE_INPUT':
      return {
        ...state,
        inputs: {
          ...state.inputs,
          [action.name]: action.value
        }
      };
    case 'CREATE_USER':
      return {
        inputs: initialState.inputs,
        users: state.users.concat(action.user)
      };
    case 'TOGGLE_USER':
      return {
        ...state,
        users: state.users.map(user =>
          user.id === action.id
            ? { ...user, active: !user.active }
            : user
        )
      };
    case 'REMOVE_USER':
      return {
        ...state,
        users: state.users.filter(user => user.id !== action.id)
      };
    default:
      throw new Error('Unhandled action');

  }
}

function App() {
  const [state, dispatch] = useReducer(reducer, initialState);
  const nextId = useRef(4);
  const { users } = state;
  const { username, email } = state.inputs;

    const onChange = useCallback(e => {
      const { name, value } = e.target;
      dispatch({
        type: 'CHANGE_INPUT',
        name,
        value
      });
    }, []);

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

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

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

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

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

export default App;