📢 어렵고 정석적인 개념 설명보다는 저같은 초보자도 이해하기 쉽게 정리하는 것을 원칙으로 포스팅하고 있습니다. 😄

[React] 복잡한 상태 관리는 useReducer()로 해결하기

What

useReducer()는 useState() 대신 사용하는 Hook 함수이며, 다수의 상태를 관리하는 데 유용한 함수이다.

useReducer() 함수를 실행하기 전, 먼저 reducer 함수를 만들어줘야 한다. reducer는 상태 객체와 행동 객체를 인자로 받아서 새로운 상태 객체를 반환하는 함수이다.

 

How

const Test = ({ num = 5 }) => {

    const reducer = (state, action) => {
        switch (action.type){
            case 'increase':
                return state + action.num;
            case 'decrease':
                return state - action.num;
            case default:
                return state;
        }
    }
    
    const [count, dispatch] = useReducer(reducer, 1) // 1은 초기값
    
    return (
        <div>
            <p>{count}</p>
            <button onClick={() => dispatch({type: 'increase', num})}>증가</button>
            <button onClick={() => dispatch({type: 'decrease', num})}>감소</button>
        </div>
    )
}

reducer는 한 마디로 swtich문으로 상태를 관리하는 것이라고 생각하면 된다.

최종적으로 다루는 state는 1개인데, 그 state의 상태 변화 함수를 이용한 함수가 많을 때 사용하면 좋다. 예를 들어서, count라는 state가 있다고 하자. 그럼, 그 count를 증가시키는 함수도 있을 것이고 감소시키는 함수도 있을 것이다. 그 함수들을 한 꺼번에 정리한 게 useReducer()이다.

 

reducer 함수를 선언할 때는 stateaction을 인자로 받아와야 한다. 그리고, switch문을 이용하여 상태 변화 함수 역할을 하고 있는 dispatch를 각각 정의한다. dispatch는 각 함수에서 데이터를 가져오는 역할을 수행한다.  type을 먼저 지정해주고, 자식 컴포넌트로부터 전달받은 데이터를 인자로 받아서 switch문에서 사용할 수 있도록 한다.

쉽게 말하면, dispatch는 먹이(데이터)를 가져오는 일개미이고, 이들이 가져온 먹이(데이터)로 switch문(개미집)에서 요리하는 것이다.

 

dispatch의 인자에는 type 이외에도 다양한 인자를 받을 수 있다. 예를 들어, 인자가 num이라고 할 때 reducer의 switch문에서 action.num으로 받아올 수 있다. 단, num이 무엇인지 사전에 선언을 해줘야한다. 위의 예시처럼 컴포넌트의 props 형식으로 num의 값을 임의로 설정해줄 수도 있다.

 

const reducer = (state, action) => {
    switch (action.type){
        case 'CREATE': {
            const todayDate = new Date().getTime();
            const newItem = {...action.data, todayDate}
            return [newItem, ...state]
        }
        default:
            return state;
    }
}

const [data, dispatch] = useReducer(reducer, []);
const dataId = useRef(0);

const onCreate = (name, content) => {
    dispatch({type: 'CREATE', data: {name, content, id: dataId.current}})
    dataId.current += 1;
}

처음 코드보다 조금 복잡한 코드의 예시이다. 원래 useReducer를 사용할 때는 더 많은 상태 변화 함수를 사용할 때지만, 예시를 위해 onCreate() 1개만 가져왔다. onCreate()는 자식 컴포넌트에게 전달되서 자식 컴포넌트의 name과 content를 부모 컴포넌트의 state에 전달하는 상태 변화 함수이다. 이것 역시 dispatch를 통해 함수의 type과 data를 reducer로 가져오고 reducer에서 state로 return 값을 전달한다.

 

1. 하나의 state를 여러 개의 함수로 여기저기 쓰일 때 하나로 정리해주는 도구가 useReducer()
2. useReducer()를 선언해주기 전, reducer 함수를 먼저 만들어주자. (reducer가 앞에 있어야 함)
3. reducer 함수는 switch문으로 구성되며 (state, action)을 인자로 받는다.
4. reducer의 return은 useReducer()의 state에 할당해주는 값이다.