React의 내장 hook 중 하나이다.
useState 처럼 상태 관리 할 때 사용됨.
주로 useState 보다 복잡한 로직에 사용한다.
현재 상태
를 말함(상태)즉 useReducer는
state로 상태를 만들고, initnalState로 초기값을 state에 넣어줌.
그 다음 action으로 필요 정보를 state에 넣고
dispatch로 action을 작동시킴, 그 다음 reducer로 state를 렌더링 시켜서
최신 것으로 바꿈
이런식의 작동이 이루어진다.
useState: 단순한 상태 값만을 다루는데 유용합니다. 간단한 상태 관리에 적합하다.
useReducer: 복잡한 상태 로직이나 여러 상태 값 간의 의존성이 있는 경우에 유용하다.
↳ state 같은 변수가 많을 때 좋음
useState: 상태를 직접 업데이트하는 setState 함수를 사용합니다. 액션에 대한 처리를 직접 작성해야 한다.
useReducer: 액션 객체를 사용하여 상태를 변경하며, 이를 처리하는 리듀서 함수를 통해 상태를 업데이트함.
액션에 대한 처리 로직이 리듀서 함수 내에 캡슐화되어 있습니다.
↳ 좀 더 코드 보기 편함(가독성 ↑), 유지보수 간편
useState: 상태 업데이트 로직이 컴포넌트 내부에 직접 작성되므로, 간단한 상태 관리에 적합하지만, 로직이 복잡해질수록 가독성이 떨어짐.
useReducer: 상태 업데이트 로직이 별도의 리듀서 함수로 분리되어 있기 때문에, 컴포넌트 내부 코드가 간결해짐.
특히 상태 업데이트 로직이 복잡한 경우에 전보다 보기 편함.
useState와 useReducer는 모두 상태 관리에 사용될 수 있지만, 성능 측면에서 큰 차이는 없다.
하지만 리듀서 함수를 사용하면 상태 업데이트 로직이 분리되므로, 특히 컴포넌트의 규모가 크거나 복잡한 경우에 유지보수성이 더 좋아짐.
미리 예시 코드를 만들어 왔다
이번에는 useReducer에서 나오는 복잡한 로직 중 무엇을 쓸 지 고민하다.
생각한 Todolist를 가볍게 만들어 볼 것이다.
(고퀄 X)
import React, { useReducer } from "react";
import styled from 'styled-components';
const Box = styled.div`
width: 50vw;
height: 10vh;
background-color: white;
display: block;
margin-left: auto;
margin-right: auto;
border: 1px solid black;
border-radius: 20px;
`
const Titlebox = styled.div`
width: 30vw;
height: 5vh;
background-color: white;
margin-left: 1vw;
margin-top: 1vh;
`
const Title = styled.span`
text-align: center;
vertical-align: middle;
font-size: 27px;
font-family: bold;
`
const Text = styled.span`
text-align: center;
vertical-align: middle;
font-family: bold;
`
const Nulldiv = styled.div`
height: 15px;
`
const Button = styled.div`
display: block;
margin-top: 2vh;
margin-left: 90vw;
`
function reducer(state, action){
switch(action.type) {
case "add":
return { count: state.count + 1 };
case "delete":
return { count: state.count - 1 };
default:
throw new Error("unsupported action type: ", action.type);
}
}
function App(){
return(
<div>
<Nulldiv />
<Box>
<Titlebox>
<Title><b>Todo list 만들기</b></Title>
<br />
<Text>Reducer 하나 배우려고 Todolist 만듭니다</Text>
</Titlebox>
</Box>
<Nulldiv />
</div>
);
}
function Todolist() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
const increaseCount = () => {
dispatch({ type: "add" });
};
const decreaseCount = () => {
dispatch({ type: "delete" });
};
const renderApps = () => {
const apps = [];
for (let i = 0; i < state.count; i++) {
apps.push(<App key={i} />);
}
return apps;
};
return (
<div>
<Button>
<button onClick={increaseCount}>증가</button>
<button onClick={decreaseCount}>감소</button>
</Button>
{renderApps()}
</div>
);
}
export default Todolist;
일단 코드를 3가지로 나눈다.
※ styled components를 사용하였음(styled component를 따로)
const Box = styled.div`
width: 50vw;
height: 10vh;
background-color: white;
display: block;
margin-left: auto;
margin-right: auto;
border: 1px solid black;
border-radius: 20px;
`
const Titlebox = styled.div`
width: 30vw;
height: 5vh;
background-color: white;
margin-left: 1vw;
margin-top: 1vh;
`
const Title = styled.span`
text-align: center;
vertical-align: middle;
font-size: 27px;
font-family: bold;
`
const Text = styled.span`
text-align: center;
vertical-align: middle;
font-family: bold;
`
const Nulldiv = styled.div`
height: 15px;
`
const Button = styled.div`
display: block;
margin-top: 2vh;
margin-left: 90vw;
`
App은 Todolist 컴포넌트이다.
어떻게 생겼는지 만들어진 것
function App(){
return(
<div>
<Nulldiv />
<Box>
<Titlebox>
<Title><b>Todo list 만들기</b></Title>
<br />
<Text>Reducer 하나 배우려고 Todolist 만듭니다</Text>
</Titlebox>
</Box>
<Nulldiv />
</div>
);
}
그냥 그대로 따라 치거나, 복붙 하면 된다.
자 reducer이다.
일단 useReducer를 사용하려면
import React, { useReducer } from "react";
로 선언을 해줘야 한다.
그리고 다음으로
파라미터안에 state, action을 선언해준다.
※ 파라미터 = ()안에 있는 것들
action은 필요 정보를 넣는 곳이라 했는데
useReducer를 사용할 때 switch문을 사용하는데
case에 결과가 달라진다(switch는)
그런 것 처럼 switch는 파라미터가 필수 인데
어떤 case인지 묻는 것이다.
우리는 action.type(액션의 종류)로 해준다.
그리고 todolist를 추가, 제거 기능을 넣기 위해
add
와 delete
case를 추가하고
마지막으로 둘 다 아닐 때는 오류가 나오게 return 해준다.
add는 state.count(지금 현 상황의 count를 의미) +1 시켜서
list가 하나 더 생긴 것으로 의미한다.
delete는 반대로 -1 시켜 하나 사라진 것을 의미한다.
function reducer(state, action){
switch(action.type) {
case "add":
return { count: state.count + 1 };
case "delete":
return { count: state.count - 1 };
default:
throw new Error("unsupported action type: ", action.type);
}
}
이번에는 Todolist 쪽이다.
여기서는 const [state, dispatch] = useReducer(reducer, { count: 0 });
를 선언한다.
해석하자면
const [state, dispatch] = useReducer(reducer, { count: 0 });
const [state, dispatch] = state와 dispatch 선언
useReducer(reducer, { count: 0});
reducer 함수 소환(dispatch한 것을 새로고침으로 표시)
count: 0 = initnalState(초기값)을 0으로 잡는다
count라는 함수의 초기값을 0으로 잡음
일단 코드에서 increaseCount
, decreaseCount
라는 변수를 선언하고
dispatch로 각각 add와 delete를 실행한다.
그리고 renderApps라는 변수는 for문을 이용해 state.count의 값만큼
Todolist = App 컴포넌트를 생성 및 제거한다.
그리고 return으로 버튼과 renderApps를 표시한다.
function Todolist() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
const increaseCount = () => {
dispatch({ type: "add" });
};
const decreaseCount = () => {
dispatch({ type: "delete" });
};
const renderApps = () => {
const apps = [];
for (let i = 0; i < state.count; i++) {
apps.push(<App key={i} />);
}
return apps;
};
return (
<div>
<Button>
<button onClick={increaseCount}>증가</button>
<button onClick={decreaseCount}>감소</button>
</Button>
{renderApps()}
</div>
);
}
초깁값이 0이라 list가 없다.
그렇다면 증가를 눌러보자
증가를 누르자 App 컴포넌트가 나온다.
증가를 누를 수록 state.count의 수가 커지고
그 수만큼 컴포넌트가 늘어난다
마찬가지로 감소를 누르면 누른 횟수만큼 state.count가 줄어
컴포넌트의 개수가 줄어든다.