DEVLOG

React Hooks에 대해 알아보자 (3) :: useReducer, 커스텀 hooks 본문

frontend/react

React Hooks에 대해 알아보자 (3) :: useReducer, 커스텀 hooks

meroriiDev 2021. 2. 9. 09:33
728x90
반응형

Hooks는 리액트 v16.8에 새로 도입된 기능으로 함수형 컴포넌트에서도 상태관리를 할 수 있는 useState, 렌더링 직후 작업을 설정하는 useEffect등의 기능을 제공하여 기존의 함수형 컴포넌트에서 할 수 없었던 다양한 작업을 할 수 있게 해준다!

 

  • useState
  • useEffect
  • useRef
  • useMemo
  • useCallback
  • useReducer
  • 커스텀 Hooks

○ useReducer

useState보다 더 다양한 컴포넌트 상황에 따라 다양한 상태를 다른 값으로 업데이트 해주고 싶을 때 사용하는 Hook

function reducer(state, action){
	return {...};
}

리듀서(Reducer)는 현재 상태, 그리고 업데이트를 위해 필요한 정보를 담은 액션값을 전달받아 새로운 상태를 반환하는 함수이다.

리듀서 함수에서 새로운 상태를 만들 때는 반드시 불변성을 지켜주어야 한다!

==> 불변성을 지키면서 업데이트한 새로운 상태를 반환(return)한다.

 

{
	type:'INCREMENT',
}

액션값은 주로 다음과 같은 형태로 이루어져있다. 나중에 다루어 볼 리덕스와 달리 useReducer에서 사용하는 액션 객체는 어떤 액션인지 알려주는 type객체를 반드시 지니고 있을 필요는 없다.

 

실습 :: 카운터구현

useReducer를 이용해서 카운터를 다시 구현해보자

import React, {useReducer} from 'react';

function reducer(state, action){
    switch(action.type){
        case 'INCREMENT':
            return {value : state.value+1};
        case 'DECREMENT':
            return {value : state.value-1};
        default:
            return state;
    }
}

const Counter = () => {
    const [state,dispatch] = useReducer(reducer, {value:0});

    return(
        <div>
            <p>
                현재 카운터 값은 <b>{state.value}</b>입니다.
            </p>
            <button onClick={()=>dispatch({type:'INCREMENT'})}>+1</button>
            <button onClick={()=>dispatch({type:'DECREMENT'})}>-1</button>
        </div>
    );
}

export default Counter;

useReducer 첫번째 파라미터에 리듀서 함수(reducer)를 넣고,

두번째 파라미터에 해당 리듀서의 기본값({value:0}을 넣어준다.

이 Hook을 이용하여 받아오는 state값은 현재 가리키고 있는 상태고, dispatch함수는 액션을 발생시키는 함수이다.

dispatch(action)의 형태로 함수 안에 파라미터로 액션값을 넣어주면 리듀서 함수가 호출되는 형태!

 

useReducer를 사용했을 때 가장 큰 장점은 컴포넌트 업데이트 로직을 컴포넌트 바깥으로 빼낼 수 있다는 것!

그래서 컴포넌트가 조각조각 분리되어있을때, state와 props만으로 값의 이동이 너무 정신이 없을때 사용하면 값을 관리하기에 좋은 것 같다.

 

실습2 :: 인풋상태관리하기

useReducer를 이용하여 인풋 상태를 관리해보자

useState에서도 인풋관리 실습을 진행했었는데 인풋이 여러개여서 useState를 여러번 사용했지만, useReducer를 사용하면 기존 클래스형 컴포넌트에서 input태그에 name값을 할당하고 e.target.name을 참조하여 setState를 해준 것과 유사한 방식으로....어쩌고 저쩌고 그렇다고 한다!

import React, {useReducer} from 'react';

function reducer(state, action){
    return{
        ...state,
        [action.name]:action.value
    };
}

const Info = () => {
    const [state, dispatch] = useReducer(reducer, {
        name:'',
        nickname:''
    });
    const {name, nickname} = state;
    const onChange = e => {
        dispatch(e.target);
    }

    return(
        <div>
            <div>
                <input name="name" value={name} onChange={onChange}/>
                <input name="nickname" value={nickname} onChange={onChange}/>
            </div>
            <div>
                <div>이름:{name}</div>
                <div>닉네임:{nickname}</div>
            </div>
        </div>
    )
}

export default Info;

이번에는 이벤트 객체가 지니고 있는 e.target값 자체를 액션값으로 이용했다.

 

사실 지금은 useReducer가 낯설어서 이걸쓴다고 뭐가 더 편한지 잘 모르겠지만...

input개수에 상관없이 아무리 많아져도 코드를 짧고 깔끔하게 유지할 수 있다는 장점이 있다고 한다! 나중에 깨닫는 날이 오겠지!

 

○ 커스텀 Hooks

여러 컴포넌트에서 비슷한 기능을 공유할 경우 커스텀 Hooks를 작성하여 로직을 재사용할 수 있다.

아직 여러 곳에서 비슷한 기능을 사용한 사례를 본적은 없지만... 하나의 경우더라도 hook로 따로 빼니 코드가 엄청 간단하고 깔끔해져서 좋은 것 같다!!

 

위에서 사용해본 Info컴포넌트를 useInputs라는 Hook으로 따로 분리해보자!

import {useReducer} from 'react';

function reducer(state, action){
	return{
    	...state,
        [action.name] : action.value
    };
}

export default function useInputs(initialForm){
	const [state, dispatch] = useReducer(reducer, initialForm);
    const onChange = e => {
    	dispatch(e.target);
    };
    return [state, onChange];
}

먼저 reducer에 관련된 로직을 따로 분리해서 Hook으로 만들어주었다!

 

import React from 'react';
import useInputs from './useInputs';

const Info = () => {
	const [state, onChange] = useInputs({
    	name : '',
        nickname : ''
    });
    const {name, nickname} = state;	//state값 비구조화할당
    
    return(
    	<div>
        	<div>
            	<input name="name" value={name} onChange={onChange}/>
            	<input name="nickname" value={nickname} onChange={onChange}/>
            </div>
            <div>
            	<p>이름 : {name} </p>
            	<p>닉네임 : {nickname} </p>
            </div>
        </div>
    );
};

export default Info;

useState사용하듯 useInput Hook을 불러와 초기값을 설정한 후

useInput에서 사용하는 state와 onChange를 꺼내와서 사용한다고 생각했더니 이해가 쉬웠다!

 

로직을 분리하여 효율적으로 코드를 관리하는 연습을 많이 해봐야겠다 :)

728x90
반응형
Comments