오늘의 나는 무엇을 잘했을까?
리액트 useState
useReducer
Hook 각각을 이해하는데 심혈을 기울였던 하루였다. 다양한 자료를 찾아보고 책으로 실습을 따라 하다보니 어느 정도 이해는 된 것 같다. 오늘처럼 한 단계씩 차근차근 공부를 해 나아가야 진정 내 것이 될 것 같다는 생각이 들었다. 진도에 너무 매몰되지 말자.
오늘의 나는 무엇을 배웠을까?
useState
를 활용하여 컴포넌트의 상태를 업데이트 할 때 업데이트 로직은 컴포넌트 내에 위치해 있어야 한다. 상태와 상태 변화 코드를 모두 컴포넌트 안에서 선언하기 때문이다. 하지만 useReducer
를 활용하면 상태를 관리할 때 상태 업데이트 로직을 컴포넌트에서 분리시켜 바깥에 작성하거나 심지어는 다른 파일에 작성 후 불러와 활용할 수도 있게 된다. 이를 상태 변화 코드의 분리
라고 말할 수 있다. 분리의 이유는 컴포넌트 내에 너무 많은 상태 변화 코드가 있으면 가독성을 해쳐 유지 보수를 어렵게 하기 때문이다.
useReducer
함수도 리액트 Hook의 한 종류이다.
useReducer
함수를 사용하는 문법을 알아보자.
const [state, dispatch] = useReducer(reducer, 0);
useReducer
함수는 인수로 상태 변화 함수인 reducer
함수와 상태의 초깃값을 받는다. 그리고 첫 요소가 상태 변수이고 두번째 요소가 상태 변화 촉발 함수 dispatch
인 배열을 반환한다. 위 코드에서는 이를 구조 분해 하여 각각 변수에 담아 주었다.
dispatch
는 첫번째 매개변수로 액션 객체를 받아 reducer
함수를 호출하여 액션 객체를 전달해 주는 함수이다.
그렇다면 reducer
함수는 무엇일까?
function reducer(state, action) {
// 새로운 상태를 만드는 로직
// const nextState = ~~
return nextState;
}
reducer는 현재 상태와 액션 객체를 매개변수로 받아 새로운 상태를 반환해 주는 함수이다. 반환된 새로운 상태는 곧 컴포넌트가 지닐 새로운 상태가 되며 컴포넌트는 해당 상태로 리렌더링 된다.
액션 객체는 업데이트를 위한 정보를 가지고 있는데 주로 type
필드와 그 외의 필요한 데이터를 가진 객체 형태로 사용된다. 예시를 살펴 보면,
{
type: 'ADD_TODO',
payload: {
id: 1,
text: '할 일 1'
}
}
위 액션 객체의 type
필드는 액션의 종류를 나타내고, payload
는 액션이 수행될 때 필요한 데이터를 담고 있다.
useReducer
함수에서는 액션 객체의 type
필드를 활용하여 어떤 작업을 수행할지 결정하고, 그 작업에 필요한 데이터를 액션 객체에 담아 reducer
함수에 전달한다.
앞서서 useReducer
함수를 사용하는 이유는 상태 업데이트 로직을 컴포넌트 외부에서 관리하기 위함이라고 정리해 보았다. 사실 이게 무슨 뜻인지 잘 와닿지 않기 때문에 각 함수를 사용한 코드를 비교해 보며 알아보자.
먼저 useState
함수를 활용한 카운터 예시이다.
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
}
const decrement = () => {
setCount(count - 1);
}
return (
<div>
<h1>{count}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
지금까지 사용해왔던 것과 동일하게 Counter 컴포넌트 내에서 increment
/ decrement
함수를 통해 상태 변화의 로직을 구현하고 있다.
다음은 useReducer
함수를 활용한 카운터이다.
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
function Counter() {
const [count, dispatch] = useReducer(reducer, 0);
const increment = () => {
dispatch({ type: 'INCREMENT' });
}
const decrement = () => {
dispatch({ type: 'DECREMENT' });
}
return (
<div>
<h1>{count}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
가장 먼저 눈에 띄는 것은 reducer
함수가 컴포넌트 외부에 선언되어 있다는 점이다. 해당 함수는 상태 변화의 로직을 담고 있으므로 결국 상태 변화의 로직이 컴포넌트 밖으로 분리되었다고 말할 수 있을 것이다.
오늘의 나는 어떤 어려움이 있었을까?
실습 위주로 공부를 하다보니 중간중간 문득 내가 잘 이해를 하고 있는 건지 단순 타이핑을 하고 있는건지 헷갈릴 때가 있었다. 개념을 어느 정도 이해하고 실습을 진행해야 겠다.
내일의 나는 무엇을 해야할까?