2021. 6. 7. 18:47ㆍFront-end/React
useReducer
리액트의 컴포넌트에서 상태를 업데이트 하기 위해서는 useState를 사용해서 상태(state)를 관리했었습니다.
그런데 useState 말고도 상태를 관리할 수 있는 방법이 있는데 바로 useReducer 입니다.
useState와 useReducer의 차이?
useReducer를 사용하면 컴포넌트 상태 업데이트 로직을 컴포넌트에서 분리시킬 수 있습니다.
상태 업데이트 로직을 컴포넌트 바깥에 작성할 수 있을 뿐만 아니라 심지어는 아예 다른 파일에 작성한 후 불러와서 사용할 수도 있습니다.
import React, { useState } from 'react';
function Counter() {
const [number, setNumber] = useState(0);
const onIncrease = () => {
setNumber(prevNumber => prevNumber + 1);
};
const onDecrease = () => {
setNumber(prevNumber => prevNumber - 1);
};
return (
<div>
<h1>{number}</h1>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
);
}
export default Counter;
✅Counter.js
이것은 기존의 useState를 이용해서 상태를 관리했었던 Counter 컴포넌트입니다.
이것을 useReducer로 변경해보는 것을 지금부터 해보겠습니다.
useReducer 알아보기
const [state, dispatch] = useReducer(reducer, initialState);
useReducer는 이런 식으로 사용하면 되는데요.
- state : 우리가 앞으로 컴포넌트에서 사용할 수 있는 상태를 가르킴
- dispatch : 액션을 발생시키는 함수, dispatch는 dispatch({type : 'INCREMENT' }) 이런 식으로 사용함
- useReducer의 첫번째 파라미터 : reducer 함수
- useReducer의 두번째 파라미터 : state의 초기 값
reducer 함수 작성하기
const reducer = (state, action) => {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
위와 같이 reducer 함수를 컴포넌트와 분리하여 작성할 수 있습니다.
useReducer를 사용하도록 고친 Counter.js
import React, { useReducer } from "react";
const reducer = (state, action) => {
switch (action.type) {
case "INCREMENT":
return state + 1;
case "DECREMENT":
return state - 1;
default:
throw new Error("Unhandled action");
}
};
function Counter() {
const [number, dispatch] = useReducer(reducer, 0);
const onIncrease = () => {
dispatch({
type: "INCREMENT",
});
};
const onDecrease = () => {
dispatch({
type: "DECREMENT",
});
};
return (
<div>
<h1>{number}</h1>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
);
}
export default Counter;
useReducer를 이용하여 함수 만들어보기
import React, { useReducer, useRef, useMemo, useCallback } from "react";
import "./App.css";
import CreateUser from "./CreateUser";
import UserList from "./UserList";
function countActiveUsers(users) {
return users.filter((user) => user.active).length;
}
const initialState = {
inputs: {
username: "",
email: "",
},
users: [
{
id: 1,
username: "velopert",
email: "public.velopert@gmail.com",
active: false,
},
{
id: 2,
username: "tester",
email: "tester@example.com",
active: false,
},
{
id: 3,
username: "liz",
email: "liz@example.com",
active: false,
},
],
};
const 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 "REMOVE_USER":
return {
...state,
users: state.users.filter((user) => user.id !== action.id),
};
case "TOGGLE_USER":
return {
...state,
users: state.users.map((user) =>
user.id === action.id ? { ...user, active: !user.active } : user
),
};
default:
throw new Error("Unhandled action");
}
};
function App() {
const [state, dispatch] = useReducer(reducer, initialState);
const nextId = useRef(4);
const {
inputs: { username, email },
users,
} = state;
const onChange = useCallback((event) => {
const name = event.target.name;
const value = event.target.value;
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 onRemove = useCallback((id) => {
dispatch({
type: "REMOVE_USER",
id,
});
}, []);
const onToggle = useCallback((id) => {
dispatch({
type: "TOGGLE_USER",
id,
});
}, []);
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle} />
<div>활성 사용자 수 : 0 </div>
</>
);
}
export default App;
만든 함수들은 CreateUser 컴포넌트나 UserList 컴포넌트로 전달이 되는데 App 컴포넌트에서 만들고 계속 재사용하면 되기 때문에 useCallback을 사용해주었습니다.
어떨때 useReducer를 쓰고 어떨 때 useState를 써야할까?
정해진 답은 없으며 상황에 따라 다릅니다.
예를 들어 컴포넌트에서 관리하는 값이 1개이고 그 값이 단순히 숫자, 문자열 등등 이라면 useState로 관리하는 것이 더 편할 것입니다.
그러나 컴포넌트에서 관리해야할 값이 많아져서 상태의 구조가 복잡해지거나 한다면 useReducer가 좀 더 편할 수 있습니다.
쉽게 생각한다면 간단한거면 useState를 사용하고 복잡하다면 useReducer를 쓰는 경우가 많은 것 같습니다.
'Front-end > React' 카테고리의 다른 글
[React] Context API를 사용하여 전역 값 관리하기 (0) | 2021.06.07 |
---|---|
[React] Custom Hook 만들기 (0) | 2021.06.07 |
[React] react-router (Switch, Link, exact, render, useHistory) (0) | 2021.05.13 |
[React] react-router에서 render 이용해서 컴포넌트 보여주기 (0) | 2021.05.13 |
[React]라우터가 아닌 컴포넌트에서 location, match, history 사용하기 - withRouter (0) | 2021.05.12 |