React ) useReducer 리듀서
useReducer는 현재 상태와 액션 개체를 매개변수로 받아와 새로운 상태를 반환해주는 함수이다.
리듀서로 상태를 관리하려면 구성 요소의 최상위에서 호출해야한다. ( 공식 문서 내용 )
import { useReducer } from 'react';
function reducer(state, action) {
// ...
}
function MyComponent() {
const [state, dispatch] = useReducer(reducer, { age: 42 });
// ...
매개변수
- reducer: 상태가 업데이트되는 방식을 지정하는 감속기 함수입니다.순수해야 하고 상태와 작업을 인수로 가져와야 하며 다음 상태를 반환해야 합니다. 상태 및 작업은 모든 유형이 될 수 있습니다.
- initialArg: 초기 상태가 계산되는 값입니다. 모든 유형의 값이 될 수 있습니다. 초기 상태가 계산되는 방법은 다음 인수에 따라 다릅니다 init.
- optional init : 초기 상태를 반환해야 하는 초기화 함수입니다. 지정하지 않으면 초기 상태로 설정됩니다 initialArg. 그렇지 않으면 초기 상태는 를 호출한 결과로 설정됩니다 init(initialArg).
useReducer는 정확히 2개의 값을 가진 배열을 반환한다.
- 현재 상태 Init ( initialArg ) 또는 initialArg ( 없는 경우 init ).
- 상태를 다른 값으로 업데이트하고 다시 렌더링을 일으키는 기능은 dispatch이다.
dispatch
- dispatch에서 반환한 함수를 사용하면 useReducer상태를 다른 값으로 업데이트하고 재렌더링을 일으킨다.
- dispatch함수에는 반환 값이 없다.
const [state, dispatch] = useReducer(reducer, { age: 42 });
function handleClick() {
dispatch({ type: 'incremented_age' });
// ...
action : 사용자가 수행할 작업. 모든 유형의 값이 될 수 있다.
사용예시 )
import { useReducer } from 'react';
function reducer(state, action) {
// ...
}
function MyComponent() {
const [state, dispatch] = useReducer(reducer, { age: 42 });
// ...
컴포넌트의 최상위에서 리듀서를 호출한다.
리듀서의 현재 상태는 우리가 정한 초기 상태로 설정된다. ( age : 42 )
function handleClick() {
dispatch({ type: 'incremented_age' });
}
상태를 업데이트 하려면 dispatch로 작업을 나타내는 type ( action ) 을 사용하여 호출한다.
React는 현재 상태와 작업을 전달하고 Reducer는 다음 상태를 계산하고 반환한다.
// App.js
import { useReducer } from 'react';
function reducer(state, action) {
if (action.type === 'incremented_age') {
return {
age: state.age + 1
};
}
throw Error('Unknown action.');
}
export default function Counter() {
const [state, dispatch] = useReducer(reducer, { age: 42 });
return (
<>
<button onClick={() => {
dispatch({ type: 'incremented_age' })
}}>
Increment age
</button>
<p>Hello! You are {state.age}.</p>
</>
);
}
Reducer 함수 작성
리듀서는 다음과 같이 선언한다.
function reducer(state, action) {
// ...
}
그 다음 상태를 계산하고 반환할 코드를 채워야한다.
관례적으로 switch 문을 작성하는 것이 일반적이라고 한다.
각각의 case에 상태를 계산하고 반환할 기능을 작성한다.
function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
return {
name: state.name,
age: state.age + 1
};
}
case 'changed_name': {
return {
name: action.nextName,
age: state.age
};
}
}
throw Error('Unknown action: ' + action.type);
}
액션은 어떤 형태든 가질 수 있다.
type 규칙에 따라 작업을 식별하는 속성과 함께 개체를 전달하는게 일반적이다.
리듀서가 다음 상태를 계산하는데 필요한 최소한의 정보를 포함하자.
function Form() {
const [state, dispatch] = useReducer(reducer, { name: 'Taylor', age: 42 });
function handleButtonClick() {
dispatch({ type: 'incremented_age' });
}
function handleInputChange(e) {
dispatch({
type: 'changed_name',
nextName: e.target.value
});
}
// ...
데이터가 여러 번 변경되더라도 각 작업은 단일 상호 작용한다.
더 많은 정보를 원하면 개발 문서를 보자 : https://ko.react.dev/learn/extracting-state-logic-into-a-reducer
state 로직을 reducer로 작성하기 – React
The library for web and native user interfaces
ko.react.dev
useState를 이용해 컴포넌트 내에서 상태 변화 함수는 컴포넌트 내에 존재해야 한다.
( 상태를 업데이트 하기 위해 기존의 상태를 참조해야 하기 때문 )
컴포넌트 내에 코드가 길어지고 무거워 지는 건 좋지않다.
이렇게 길고 복잡한 상태 변화 로직을 컴포넌트 밖으로 분리하자.
UseReducer를 이용하면 상태 변화 로직들을 컴포넌트에서 분리할 수 있게 되어
컴포넌트를 가볍게 작성 가능하다.
useState를 이용하면 위와 같이 상태 변화 함수를
각각 Counter 컴포넌트 안에 작성해야하고 코드가 길어지고 무거워진다.
위처럼 useReducer를 이용하면 reducer상태 변화 함수를 컴포넌트 밖에 분리하여
쉽게 처리할 수 있게 만들 수 있다.
배열을 반환하고 비구조 할당을 통해 사용한다.
const Counter = () => {
// ┌state의 초기값
const [count, dispatch] = useReducer(reducer, 1);
// └state └상태변화 action └상태변화 처리
...
}
// 상태와, 액션을 가져와 어떤 타입인지 case로 찾아 실행한다.
function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
return {
name: state.name,
age: state.age + 1
};
}
case 'changed_name': {
return {
name: action.nextName,
age: state.age
};
}
}
throw Error('Unknown action: ' + action.type);
}
내가 보기 위해 열심히 작성함.