useReducer란?
useReducer 기본 형태
[ const [state, dispatch ] = useReducer(reducer, initialState); ]
<예시 코드>
import { useReducer } from 'react';
function reducer(state, action) {
// ...
}
function MyComponent() {
const [state, dispatch] = useReducer(reducer, { age: 42 });
// ...
매개변수
reducer : reducer 함수는 상태가 업데이트되는 방식을 지정
initialArg : 초기 상태가 계산되는 값
init 인자에 의해 결정됨선택적 init : 초기 상태를 계산하는 초기화 함수
init 함수가 지정되지 않는 경우, 초기 상태는 initialArg 값으로 설정 init 함수가 지정된 경우, 초기 상태는 init(initialArg) 호출 결과로 설정반환값
init(initialArg) 또는 initialArg로 설정주의사항
사용법
컴포넌트에 추가하기
- 컴포넌트 최상위 레벨에서 useReducer를 호출하여 reducer를 state 관리한다.
import { useReducer } from 'react'; function reducer(state, action) { // ... } function MyComponent() { const [state, dispatch] = useReducer(reducer, { age: 42 }); // ...
- useReducer는 정확히 두 개의 항목이 있는 배열을 반환한다.
- 이 state 변수는 현재 state -> 처음에 제공한 초기 state로 설정됨
- 상호작용에 반응하여 이를 변경할 수 있는 dispatch 함수
- 화면에 표시되는 내용을 업데이트 하려면 사용자가 수행한 작업을 나타내는 객체,
즉, 액션을 사용하여 dispatch를 호출function handleClick() { dispatch({ type: 'incremented_age' }); }
- React는 현재 state와 액션을 reducer 함수에 전달한다.
- Reducer는 다음 state를 계산하고 반환한다.
- React는 다음 state를 저장하고, 컴포넌트를 렌더링하고, UI를 업데이트 한다.
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 함수 작성하기
- Reducer 함수는 다음과 같이 선언된다.
function reducer(state, action) { // ... }
- 그런 다음 state를 계산하고 반환할 코드를 입력해야 한다.
- 관례상 switch 문으로 작성하는 것이 일반적이다.
- switch의 각 case에 대해 다음 state를 계산하고 반환해야 한다.
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 프로퍼티가 있는 객체를 전달하는 것이 일반적이다.
- 여기에는 reducer가 다음 state를 계산하는데 필요한 최소한의 필수 정보가 포함되어야 한다.
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 }); } // ...
- 액션 유형 이름은 컴포넌트에 로컬로 지정된다
- 각 액션은 아무리 많은 데이터를 변경하게 되더라도 오직 하나의 상호작용만을 기술
- state의 모양은 임의적이지만 일반적으로 객체나 배열이 될 것 같다
초기 state 재생성 방지하기
- React는 초기 state를 한 번 저장하고 다음 렌더링에서 이를 무시한다.
function createInitialState(username) { // ... } function TodoList({ username }) { const [state, dispatch] = useReducer(reducer, createInitialState(username)); // ...
- createInitialState(username)의 결과는 초기 렌더링에서만 사용되지만, 이후의 모든 렌더링에서도 여전히 이 함수를 호출하게 된다
- 이는 큰 배열을 만들거나 값비싼 계산을 수행하는 경우 낭비가 될 수 있다.
- 이 문제를 해결하려면 useReducer 세번 째 인수에 초기화 함수를 전달할 수 있다.
function createInitialState(username) { // ... } function TodoList({ username }) { const [state, dispatch] = useReducer(reducer, username, createInitialState); // ...
- 함수를 호출한 결과인 createInitialState()가 아니라 함수 자체 createInitialState를 전달하고 있다는 점에 유의해야 한다.
- 이렇게 하면 초기화 후에는 초기 state가 다시 생성되지 않는다.
- 위의 예에서 createInitialState는 username 인수를 받는다.
- 초기화 함수가 초기 state를 계산하는 데 아무런 정보가 필요 하지 않는 경우, useReducer의 두번째 인수로 null을 전달할 수 있다.
오잉 ? ^-^ 이라고 할 뻔 했습니다~