[React] useReducer Hook 알아보기

2023. 4. 7. 00:53WEB Dev/Javascript | REACT | Node.js

728x90

 

이 예제는 Udemy의 React 완벽 가이드 강의에서 발췌하였습니다.

 


 

 

📌 들어가며

상태를 관리하다보면 상태를 변경하는 핸들러 함수가 무한히 많아지는 현상이 일어나고 그에 따라 상태도 같이 늘어난다. 그때마다 useState를 호출해서 하나씩 상태를 관리하기란 쉽지 않다.

이를 위해 리액트에게는 useReducer라는 강력한 무기가 있는데, 강력한 만큼 useState처럼 쉽게 쓸 수 없는 만만치 않은 훅이라 별도로 공부를 해본다.

 

 

📌 useReducer란

리액트의 상태 관리를 위한 훅(Hook) 중 하나로,

복잡한 상태를 관리하거나 상태 변경 로직을 별도로 분리할 필요가 있는 경우에 사용한다.

 

 

useState로 많은 상태를 관리하는 것보다 useReducer로 상태를 묶어 관리하면 가독성 있는 상태 관리 코드를 볼 수 있다.

 

 

📌 useReducer의 원리

 

useReducer 훅은 state와 dispatch 함수를 반환한다.

state는 현재 상태를 나타내고, dispatch 함수는 상태를 변경하는 액션(action)을 발생시킨다.

이 때, dispatch 함수는 Reducer 함수와 함께 사용된다.

dispatch 함수를 호출하면 Reducer 함수가 실행되고, 이전 상태와 액션 객체를 받아서 새로운 상태를 반환한다. 

이후, 리액트는 반환된 새로운 상태를 컴포넌트에 반영하고, 컴포넌트를 다시 렌더링한다.

 

 

 

📌 useReducer를 사용해야 하는 경우

 

-  복잡한 상태관리가 필요한 경우

    - 여러 개의 연관된 상태를 한 번에 업데이트 해야 할 때

    - 이전 상태를 참조해 새로운 상태를 업데이트 해야 할 때

    - 상태 업데이트에 복잡한 로직을 사용해야 할 때

 

위와 같은 경우에 useState 대신 useReducer를 이용해 상태를 업데이트 시킬 수 있다.

 

 

 

📌 useReducer 사용하기

 

 

1. Reducer 함수를 만든다.

 

이 함수에는 두 개의 인자가 필요하다. 리액트에서 자동으로 전달하는 이전 상태에 대한 state와 action이다.

action은 추후 새로운 상태를 전달하게 된다.

const stateReducer = (state, action) => {
  return stateReducer;
}

 

 

2. 초깃값을 만든다.

Reducer 함수로 반환될 상태의 초깃값으로 별도의 변수로 선언하여 관리해주면 조금 더 가독성 있는 코드로 보일 수 있다.

/*기존 useState*/

const [value, setValue] = useState('');
const [isTouched, setIsTouched] = useState(false);


-------------------------------------

/*useReducer 적용*/

const initialState = {
  value: '',
  isTouched: false,
}

 

 

 

3. useReducer 훅을 선언한다.

useReducer을 선언하고 위에서 만든 Reducer 함수와 초깃값을 넣어준다. 

useReducer(stateReducer, initialState);

 

그리고 구조분해할당으로 2개의 변수로 받을 수 있다.

첫 번째 요소는 Reducer 함수에 의해 결정되는 '상태'로 state라는 변수로 받아보고

두 번째 요소는 dispatch(파견, 발송) '함수'로 Reducer 함수에서 실행할 함수이다. 

const [state, dispatch] = useReducer(stateReducer, initialState);

 

 

 

4. useReducer 훅을 사용한다.

 

이제 기존에 useState로 관리했던 값들을 변경해준다.

useReducer를 통해 받아오는 initialState를 state라는 변수로 받고 있기 때문에 해당 변수를 필요한 곳에 넣어준다. 

 

/*기존*/

const valueIsValid = validateValue(enteredValue);
const hasError = !valueIsValid && isTouched;


---------------------------------------------------------------


/*useReducer로 변경*/

const [state, dispatch] = useReducer(stateReducer, initialState);

const valueIsVaild = validateValue(state.value);
const hasError = !valueIsValid && state.isTouched;

 

 

그리고 dispatch 함수를 이용해 Handler 함수들을 변경한다.

일반적으로는 객체 안에 type이라는 key-value 쌍을 전달해 실행할 분기를 구분한다.

여기서는 각각 onChange와 onBlur에 대한 Handler와 해당 Input들이 Submit 되고 나서 실행될 Reset 함수가 있어

 type의 값으로 'CHANGE', 'BLUR', 'RESET'을 주었다.

 

/*기존*/

const valueChangeHandler = (event) => {
  setEnteredValue(event.target.value);
}
const inputBlurHandler = () => {
  setIsTouched(true);
}
const reset = () => {
  setEnteredValue('');
  setIsTouched(false);
}

--------------------------------------------------------------------

/*useReducer로 변경*/

const valueChangeHandler = (event) => {
  dispatch({ type: 'CHANGE', value: event.target.value });
}
const inputBlurHandler = () => {
  dispatch({ type: 'BLUR'})
}
const reset = () => {
  dispatch({ type: 'RESET' })
}

 

이제 해당 분기들을 가지고 reducer 함수에서 실행할 함수들을 만들어 준다. 

각각의 경우에 필요한 state 정보를 수정하는 set 함수들을 만들어준다.

업데이트 하지 않는 state는 기존 state를 받아오는 인자인 state에서 값을 받아와 그대로 사용하도록 한다.

 

const stateReducer = (state, action) => {

  if(action.type === 'INPUT'){
    return { value: action.value, isTouched: state.isTouched }
  }
  if(action.type === 'BLUR'){
    return { value: state.value, isTouched: true }
  }
  if(action.type === 'RESET'){
    return { value: '', isTouched: false }
  }

}

 

 

 

 

 

 

📌 출처 및 참고문서

 

- 리액트 공식문서

728x90