useReducer 란
- state(상태) 를 관리하고 업데이트 하는 hook 인 useState 를 대체할 수 있는 Hook
- useState 처럼 state를 관리하고 업데이트 할 수 있는 hook
useReducer 의 매력
- 한 컴포넌트 내에서 state 를 업데이트 하는 로직 부분을 그 컴포넌트로부터 분리시키는 것을 가능하게 해줌
=> state 업데이트 로직을 분리하며 컴포넌트 외부에 작성하는 것을 가능하게 함으로써, 코드의 최적화를 이루게 해줌.
useReducer 와 useState 의 사용 차이
useState
- 관리해야 할 state 가 1개일 경우
- state가 단순한 숫자, 문자열 또는 Boolean 값일 경우
useReducer
- 관리해야 할 state 가 1개 이상, 복수일 경우
- 현재는 단일 state 값을 관리하지만, 추후 유동적일 가능성이 있을 경우
- 스케일이 큰 프로젝트의 경우
- state 의 구조가 복잡해질 것으로 보이는 경우
useReducer 에서 알아야 할 4가지
useReducer
- 첫 번째 인자인 reducer 함수가 반환하는 값으로 state 를 갱신하는 역할
const [state, dispatch] = useReducer(reducer, initialState, init);
state : 컴포넌트에서 사용할 상태
initialState : 초기 state
init : 초기 함수
dispatch
- state 업데이트를 위한 요구
- reducer 함수를 실행시키며, 컴포넌트 내에서 state 의 업데이트를 일으키기 위해서 사용하는 함수
- action 을 이용하여, 컴포넌트 내에서 state 의 업데이트를 일으키기 위해서 사용함
dispatch({ type: "decrement" })
action
- 요구의 내용
- 업데이트를 위한 정보를 가지고 있는 것
- Dispatch 의 인자가 되며, reducer 함수의 두 번째 인자인 action 에 할당됨
- 주로 type 이라는 값을 지닌 객체 형태로 사용됨
{ type: "decrement" }
reducer
- 컴포넌트 외부에서 state 를 업데이트 하는 로직을 담당하는 함수
- 현재의 state 와 action 을 인자로 받음
- action 의 값에 근거하여 기존의 state 를 대체할 새로운 state 를 반환한다
function reducer(state, action) {
switch (action.type) {
...
}
기존의 state 를 변경하거나 , 추가하거나, 덮어쓰지 않는다
useReducer 예제
은행 예제
const ACTION_TYPES = {
deposit : "deposit",
withdraw : "withdraw"
}
const reducer = (state, action) => {
switch(action.type) {
case ACTION_TYPES.deposit :
return state + action.payload
case ACTION_TYPES.withdraw :
return state - action.payload
default :
return state;
}
}
function App() {
const [number, setNumber] = useState(0);
const [money, dispatch] = useReducer(reducer, 0)
return (
<div>
<h2>useReducer 의 은행에 오신것을 환영합니다.</h2>
<p>잔고 : {money}원</p>
<input
type="number"
value={number}
onChange={(e) => setNumber(parseInt(e.target.value))}
step="1000"
/>
<button onClick={() => {
dispatch({type : ACTION_TYPES.deposit , payload : number})
}}
>예금</button>
<button onClick={() => {
dispatch({type : ACTION_TYPES.withdraw, payload: number})
}}>출금</button>
</div>
);
}
- money 라는 state 를 만들어주고, dispatch 를 사용해 그 state 를 어떻게 업데이트 할 것인지 정해준다.
- reducer 라는 함수를 만들어주고, 현재 state 와 action 을 인자로 받아준다.
- money 가 현재 state 가 된다.
- reducer 에서 type 을 switch 의 case를 통해 dispatch 에서 고른 type 별로 이벤트를 추가해주는 것이라고 생각하는게 편하다.
- action 의 type 이름은 ACTION_TYPES 와 같은 constant 를 따로 만들어주어서 관리하면 편리하다.
출석부 예제
App.js
const reducer = (state, action) => {
switch(action.type){
case 'add-student' :
const name = action.payload.name;
const newStudent = {
id : Date.now(),
name,
isHere : false,
}
return {
count : state.count + 1,
students : [...state.students, newStudent]
}
case 'delete-student' :
return {
count : state.count - 1,
students : state.students.filter(
(student) => student.id !== action.payload.id
)
}
case 'mark-student' :
return {
count: state.count,
students : state.students.map((student) => {
if(student.id === action.payload.id){
return {...student, isHere : !student.isHere}
}
return student;
})
}
default :
return state;
}
}
const initialState = {
count : 0,
students : []
}
function App() {
const [name, setName] = useState("");
const [studentsInfo , dispatch] = useReducer(reducer, initialState)
return (
<div>
<h1>출석부</h1>
<p>총 학생 수 : {studentsInfo.count}</p>
<input
type="text"
placeholder='이름을 입력해주세요'
value={name}
onChange={(e) => setName(e.target.value)}
/>
<button onClick={() => {
dispatch({type: 'add-student', payload: {name}})
}}>추가</button>
{studentsInfo.students.map((student) => {
return <Student key={student.id} name={student.name} dispatch={dispatch} id={student.id} isHere={student.isHere} />
})}
</div>
);
}
- useReducer 를 호출할 때, studentsInfo 를 현재 상태 (state) 로 정해준다. 이 state 는 initialState 와 같다.
- initialState 에는 초기값을 넣어주도록 한다. ( 학생 수, 학생 정보 )
- action.type 에는 아래와 같이 3가지로 구성이 되어있다.
"add-student" : 학생의 정보를 추가하는 action
"delete-student" : 클릭한 학생의 id 와 데이터의 id 가 같은 것이 있을 경우 그 데이터를 삭제하는 action
"mark-student" : 학생 이름을 클릭했을 때 학생의 이름에 줄이 가게끔 하는 action (isHere 의 값을 변경시키는 action)
- 학생을 추가하거나 제거할 때는 count 의 값을 빼거나 더하게 해서, 학생의 수를 알게 해주었다.
Student.jsx
import React from "react";
function Student({ name, dispatch, id, isHere }) {
return (
<div>
<span
style={{
textDecoration: isHere ? "line-through" : "none",
color: isHere ? "gray" : "black",
}}
onClick={() => {
dispatch({ type: "mark-student", payload: { id } });
}}
>
{name}
</span>
<button
onClick={() => {
dispatch({ type: "delete-student", payload: { id } });
}}
>
삭제
</button>
</div>
);
}
export default Student;
- 학생의 이름을 클릭하면 dispatch 를 사용해, mark-student 이벤트를 실행할 수 있도록 했다. ( 이름에 줄이 가는 action )
- 스타일도 그에 맞게끔 삼항연산자를 사용해 변경되게 해주었다.
- 삭제 버튼을 클릭하면 dispatch 를 사용해, delete-student 이벤트를 실행할 수 있도록 했다. ( 학생 정보를 삭제하는 action )
- payload 는 모두 현재 학생의 id 를 알 수 있게 하기위해서 현재 학생의 id 를 값으로 넣어주었다.
'React > 이론공부' 카테고리의 다른 글
React.Fragment (0) | 2023.01.25 |
---|---|
React.memo (0) | 2023.01.25 |
[React Hooks] useCallback (0) | 2023.01.25 |
[React Hooks] useMemo (0) | 2023.01.24 |
[React Hooks] useContext & Context (0) | 2023.01.24 |