상세 컨텐츠

본문 제목

[리액터 스타터3] 7장 useReducer와 상태 관리 / 8장 최적화

23-24/React.js 3

by YUZ 유즈 2023. 12. 29. 10:00

본문

728x90

7장 useReducer와 상태 관리

 

useReducer 이해하기

  • 상태 변화 코드를 쉽게 분리할 수 있다.

실습 준비하기

1. component > TestComp.js 생성

import {useState} from "react";

function TestComp() {
    const [count, setCount] = useState(0);

    const onIncrease = () => {
        setCount(count+1);
    };

    const onDecrease = () => {
        setCount(count-1);
    };

    return (
        <div>
            <h4>테스트 컴포넌트</h4>
            <div>
                <bold>{count}</bold>
            </div>
            <div>
                <button onClick={onIncrease}>+</button>
                <button onClick={onDecrease}>-</button>
            </div>
        </div>
    );
}

export default TestComp;
  • + 버튼 : onIncrease 함수 호출 → count 값 +1
  • - 버튼 : onDecrease 함수 호출 → count 값 -1

 

2. TestComp를 App 컴포넌트의 자식으로 배치

import TestComp from './component/TestComp'; // TestComp import


return(
    <div className='App'>
      <TestComp /> //자식으로 설정
 		(...)
    </div>
  );
}

export default App;

상태 변환 코드란?

  • State 값을 변경하는 코드
    • onIncrease함수 : 카운트를 1 증가함
    • onDecrease 함수 : 카운트를 1 감소함

 

상태 변화 코드를 컴포넌트에서 분리한다
  • 컴포넌트 내부에 작성했던 상태 변화 코드를 외부에 작성한다는 뜻

useState를 이용해 State를 생성하면 상태변화 코드는 컴포넌트 안에 작성해야 한다.

But, useReducer을 사용하면 상태 변화 코드를 컴포넌트 밖으로 분리할 수 있다.

 

 

useReducer의 기본 사용법

  • 리액트 컴포넌트에서 State를 관리하는 리액트 훅
  • 컴포넌트 내부가 아닌 외부에서 관리 가능

1. TestComp에서 useState로 만든 기능 모두 삭제

function TestComp() {
    return (
        <div>
            <h4>테스트 컴포넌트</h4>
            <div>
                <bold>0</bold>
            </div>
            <div>
                <button>+</button>
                <button>-</button>
            </div>
        </div>
    );
}

export default TestComp;

 

 

2. useReducer 활용하기

import { useReducer } from "react"; // react 라이브러에서 import

function reducer() {} //새로운 함수 reducer를 컴포넌트 밖으로 만듦

function TestComp() {
    const [count, dispatch] = useReducer(reducer, 0); //useReducer 함수 호출
    
    return (
       (...)
    );
}

export default TestComp;
const [count, dispatch] = useReducer(reducer, 0)
  • count : state 변수
  • dispatch : 상태 변화 촉발 함수
  • 첫 번째 인수 : 상태 변화 함수(reducer)
  • 두 번째 인수 : State 초깃값(0)

 

3. count 를 페이지에 렌더링, 버튼을 눌렀을 때 카운트 늘리거나 줄이기

import { useReducer } from "react";

function reducer() {}

function TestComp() {
    const [count, dispatch] = useReducer(reducer, 0);

    return (
        <div>
            <h4>테스트 컴포넌트</h4>
            <div>
                <bold>{count}</bold> //count 렌더링
            </div>
            <div>
                <button onClick={() => dispatch({type: "INCREASE", data: 1})}> 
				//<+>버튼을 누르면 함수 dispatch를 호출하고 인수로 객체를 전달한다.
                +
                </button>
                <button onClick={() => dispatch({type: "DECREASE", data: 1})}>
				//<->버튼을 누르면 함수 dispatch를 호출하고 인수로 객체를 전달한다.
                -
                </button>
            </div>
        </div>
    );
}

export default TestComp;

 

  • dispatch 함수는 인수로 객체를 전달함
  • 이 객체는 State의 변경 정보를 담고 있다.
  • 객체 = "action 객체"

 

<+> 버튼을 눌렀을때

  • dispatch는 2개의 프로퍼티로 이루어진 action 객체를 인수로 전달
    • 첫번째 프로퍼티 type : 어떤 상황이 발생했는지 나타냄(증가-INCREASE)
    • 두번째 프로퍼티 data : 상태 변화에 필요한 값(+1)

 

  • 실제 상태 변화 함수 reducer 에서 일어난다.
    • dispatch함수 호출 → reducer 함수 호출 → State 값 업데이트

 

4. TestComp에서 함수 reducer 작성

function reducer(state, action) {//state : 현재 State 값이 저장, action : dispatch의 action객체 전달
    switch(action.type){
        case "INCREASE":
            return state + action.data;//기존 State에 action객체의 data 값을 더해 반환
        case "DECREASE":
            return state - action.data;//기존 State에 action객체의 data 값을 빼서 반환
        default:
            return state;
    }
}

 

 

5. 새로운 상태변화 추가하기

  • 0으로 초기화 상태 추가하기
function reducer(state, action) {
    switch(action.type){
        case "INIT": //초기화 상태 추가하기
            return 0;
       (...)
    }
}


function TestComp() {
    const [count, dispatch] = useReducer(reducer, 0);

    return (
        <div>
            <h4>테스트 컴포넌트</h4>
            <div>
                <bold>{count}</bold>
            </div>
            <div>
               (...)
                <button onClick={() => dispath({type: "INIT"})}> //카운트를 0으로 초기화 하는 버튼 생성
                0으로 초기화
                </button>
            </div>
        </div>
    );
}

export default TestComp;

 

 

[할 일 관리] 앱 업그레이드

useState를 useReducer로 바꾸기

  • App.js
import {useRef, useReducer} from "react"; //useReducer import(useState 모두 삭제)

function reducer(state, action) { //state 반환하도록 작성(상태 변화 코드는 찬찬히..)
	//상태 변화 코드
  return state;
}

function App() {

  (...)
  const[todo, dispatch] = useReducer(reducer, mockTodo); //useReducer로 대체

   return(
   (...)
  );
}

export default App;

 

  • setTodo 대체 : dispatch 함수를 호출
  const onCreate = (content) => { //setTodo를 모두 삭제
    idRef.current += 1;
  };

  const onUpdate = (targetId) => {
  };

  const onDelete = (targetId) => {
  };

 

 

Create: 할 일 아이템 추가하기

  • useReducer로 [할 일 관리] 앱의 기본인 아이템 추가 기능을 구현하기

1. onCreate에서 dispatch 호출, 인수로 할 일 정보를 담은 action 객체 전달하기

  • App.js
  const onCreate = (content) => {
    dispatch({ //새 할 일 아이템을 생성하기 위한 dispatch 함수 호출
      type: "CREATE", 
      newItem: { // 추가할 할 일 데이터 설정
        id: idRef.current,
        content,
        isDone: false,
        createdDate: new Date().getTime(),
      },
    });
    idRef.current += 1;
  };

 

2. reducer에서 action 객체의 type이 CREATE일 때, 새 아이템을 추가하는 상태 변화 코드 작성하기

  • App.js
function reducer(state, action) {
  switch(action.type){ //tpye 별로 케이스 나누기
    case "CREATE": 
      return [action.newItem, ...state]; //기존 할 일 아이템에 action객체의 아이템이 추가된 새 배열을 반환한다.

    default:
      return state;
  }
}

 

<호출 과정>

1. dispatch를 호출하여 인수로 action 객체 전달

2. reducer 함수의 반환값으로 State가 업데이트

3. 할 일 아이템 추가

 

 

 

Update: 할 일 아이템 수정하기

  • onUpdate 수정하기(App.js)
  const onUpdate = (targetId) => {
    dispatch({ 
      type: "UPDATE",
      targetId, //targetId 프로퍼티에는 체크 여부로 수정할 아이템의 id를 설정
    });
  };
function reducer(state, action) {
  switch(action.type){
    case "CREATE":
      return [action.newItem, ...state];
    case "UPDATE" : {
      return state.map((it) =>  //map 메서드로 순회하면서 매개변수 state에 저장된 아이템 배열에서
      it.id === action.targetId //id 값을 비교하여
      ?{                       // 일치하면
        ...it,                 
        isDone: !it.isDone,	   // isDone을 토글한 새 배열을 반환한다.
      }
      : it
      );
    }
    default:
      return state;
  }
}

 

 

 

Delete: 할 일 삭제 구현하기

  • onDelete 수정하기(App.js)
  const onDelete = (targetId) => {
    dispatch({ 
      type: "DELETE",
      targetId, //targetId 프로퍼티는 삭제할 아이템의 id 를 설정
    });
  };

function reducer(state, action) {
  switch(action.type){
    (...)

    case "DELETE": { //filter 메서드로 id와 targetId가 일치하는 할 일 아이템만 제외한 할 일 배열을 반환
      return state.filter((it) => it.id !== action.targetId);
    }
    
    default:
      return state;
  }
}

 


8장 최적화

최적화와 메모이제이션

  • 최적화 : 웹 서비스의 기능을 개선하는 기술
    • 불필요하게 낭비되는 연산을 줄여 렌더리의 성능을 높이는 방법
    • 최적화 방법 : 코드, 폰트, 이미지 파일 크기를 줄이는 기술
    • '리액트의 연산 낭비'를 줄이는 것에 초점
  • 메모이제이션(Memoization) 기법 : 연산 최적화 방법
    • '메모하는 방법'
    • 특정 입력에 대한 결과를 계산해 메모리 어딘가에 저장했다가, 동일한 요청이 들어오면 저장한 결괏값을 제공해 빠르게 응답하는 기술 => 동적 계획법(Dynamic Programming)

 

 

함수의 불필요한 재호출 방지하기

  • useMemo : 메모이제이션 기법을 이용해 연산의 결괏값을 기억했다가 필요할 때 사용함으로써 함수 호출을 막아주는 리엑트 

할 일 분석 기능 추가하기

  • TodoList 컴포넌트에서 할 일 아이템을 분석하는 기능 추가하기
    • 추가한 할 일 아이템이 모두 몇 개인지, 또 완료 아이템과 미완료 아이템이 몇 개인지 검색해 페이지에 렌더링
    const analyzeTodo = () => { // 새로운 analyzeTodo 작성
        const totalCount = todo.length; //todo(State 변수)의 아이템 총 개수
        const doneCount = todo.filter((it) => it.Done).length; //완료된 아이템 개수
        const notDoneDount = totalCount - doneCount; // 미완료된 아이템 개수
        return { //객체에 담아 반환
            totalCount,
            doneCount,
            notDoneDount,
        };
    };

 

  • 함수 analyzeTodo를 호출하고 반환값을 페이지에 렌더링하기
const {totalCount, doneCount, notDoneDount} = analyzeTodo(); //analyzeTodo 호출하고 반환 객체를 구조분해 할당


   (...)
    return(
        <div className="TodoList">
            <h4>TodoList 🌱</h4>
            <div>
                <div>총개수: {totalCount}</div> //totalCount 렌더링
                <div>완료된 할 일: {doneCount}</div>
                <div>아직 완료하지 못한 할 일: {notDoneCount}</div>
            </div>
            
  (...)

 

 

 

문제점 파악하기

  • analyzeTodo 함수는 todo에 저장된 아이템 개수에 비례해 수행할 연산량이 증가한다.
  • todo에 저장한 아이템 개수가 많아지면 성능상의 문제 발생 !!

 

  • 불필요한 analyzeTodo 함수 호출이 일어나는지 확인하기
const TodoList = ({ todo, onUpdate, onDelete }) => {

    const analyzeTodo = () => {
        console.log("analyzeTodo 함수 호출"); // 함수 호출하기
        (...)
}
  • TodoList 컴포넌트의 검색 폼에 "react" 검색하기
    • "analyzeTodo 함수 호출"이 6번 출력됨
    • TodoList 컴포넌트가 처음 마운트 → 1번
    • 검색 폼에서 react 다섯 글자 입력할 때마다 TodoList가 리렌터 → 5번

→ 컴포넌트 내부에서 선언한 함수는 렌더링할 때마다 실행된다.

State 변수 search가 업데이트 되면서 TodoList 컴포넌트가 리렌더되고, 내부에 선언된 analyzeTodo또한 다시 호출된다.

 

 

useMemo 를 이용해 [할 일 관리] 앱 최적화 하기

  • 불필요하게 analyzeTodo 함수 호출을 하지 않도록 하기

1. useMemo의 기본 사용법

const value = useMemo(callback, deps);

 

  • callback : 콜백 함수
  • deps : 의존성 배열
const value = useMemo(() => { //첫번째 인수로 콜백함수 전달(의존성 배열의 값이 안달라지면 호출X)
	return count * count;
}, [count]); //count 값이 변하면 콜백함수를 다시 호출해 변경된 반환값을 value에 저장함.

 

2. 함수 analyzeTodo의 재호출 방지하기

  • TodoList.js 수정하기
import { useMemo, useState } from "react"; //useMemo react 라이브러리에서 불러오기    


	const analyzeTodo = useMemo(() => { //useMemo 호출 후, 첫번째 인수:analyzeTodo 함수 전달
        console.log("analyzeTodo 함수 호출");
        const totalCount = todo.length;
        const doneCount = todo.filter((it) => it.Done).length;
        const notDoneCount = totalCount - doneCount;
        return {
            totalCount,
            doneCount,
            notDoneCount,
        };
    }, [todo]); //두번째 인수로 todo (todo값이 변할 때마다 analyzeTodo함수 호출)

const {totalCount, doneCount, notDoneCount} = analyzeTodo;
    //useMemo는 함수가 아닌 값을 반환하므로 함수 analyzeTodo에는 값이 저장된다.
    // 구조분해 할당의 대상을 기존 analyzeTodo()가 아닌 analyzeTodo로 변경

 

독서하기 입력

  • 검색어를 입력해도 analyzeTodo 함수가 호출되지 않는다

독서하기 추가

  • 독서하기를 추가하여 todo값이 업데이트되어 useMemo는 연산을 다시 수행하여 analyzeTodo 함수가 호출된다.

 

다음 실습을 위해 "analyzeTodo 함수 호출"을 콘솔에 출력하는 코드는 삭제하기

 

 

불필요한 컴포넌트 리렌더 방지하기

  • React.memo :  리엑트의 최적화 기능
    • 메모이제이션 기법으로 컴포넌트가 불필요하게 리렌더되는 상황을 방지할 수 있다.

 

고차 컴포넌트와 횡단 관심사

  • 고차 컴포넌트
    • 인수로 전달된 컴포넌트를 새로운 컴포넌트로 반환하는 함수이다.
    • 고차 컴포넌트는 전달된 컴포넌트를 그대로 반환하는게 아니라 어떤 기능을 추가해 반환한다.

withFunc를 이용해 '기능 A'라는 컴포넌트를 감싼 다음, 새 기능이 추가된 강화된 컴포넌트를 반환하는 예

 

  • 기능을 추가해 반환한 컴포넌트를 "강화된 컴포넌트"라고 한다.
  • const EnhancedComp = witdhFunc(Comp);

 

  • 횡단 관심사(Cross-Cutting Concerns)
    • 고차 컴포넌트를 이용하면 황단 관심사 문제를 해결할 수 있다.
    • 프로그래밍에서 비즈니스 로직(핵심 기능)과 구분되어 공통 기능을 지칭할 때 사용하는 용어이다.
    • 2가지 예시
const CompA = () => {
	console.log("컴포넌트가 호출되었습니다."); //횡단 관심사1
	returb <div>CompA</div>;
};

const CompB = () => {
	console.log("컴포넌트가 호출되었습니다."); //횡단 관심사2
	returb <div>CompB</div>;
};

 

  • CompA와 CompB의 황단 관심사 1,2는 각각 컴포넌트의 핵심 기능이 아닌 여러 컴포넌트에서 공통으로 사용하는 기능이다.
  • 황단 관심사의 예시
    • 로깅, 데이터 베이스 접속, 인가 등 → 여러 곳에서 호출해 사용하는 코드들

 

  • 그림[8-6]
    • 컴포넌트의 핵심 기능(비즈니스 기능)을 세로로 배치한다.
    • 여러 컴포넌트에서 공통으로 사용하는 기능(횡단 관심사)를 가로로 배치한다.
    • 공통 기능들이 핵심 컴포넌트들을 마치 "횡단"하는 모습이다.
    • 모든 컴포넌트가 마운트와 동시에 콘솔에 특정 메세지를 출력하는 기능은 핵심 로직이 아니다.
      공통으로 사용하는 기능 :  "횡단 관심사"'
      횡단 관심사는 중복된 코드가 만들어진다.
  • 고차 컴포넌트를 이용하면 횡단 관심사 코드를 함수로 분리할 수 있다.
function withLifecycleLogging(WrappedComponent) { //인수로 컴포넌트를 받는다 => 래핑된 컴포넌트
	return (props) => {
		useEffect(() => {
			console.log("Mount!"); // 마운트 출력
			return () => console.log("Unmount!"); //언마운트 출력
		}, []);
		useEffect(() => {
			console.log("Update!"); //업데이트 출력
		});
		return <WrappedComponont {...props} />; //강화된 컴포넌트 반환
	};
}

 

const LifecycleLoggingComponent = withLifecycleLogging(Comp);

Comp : 래핑된 컴포넌트(인수로 받는 컴포넌트)
LifecycleLoggingComponent : 강화된 컴포넌트
withLifecycleLogging : 고차 컴포넌트
  • 고차 컴포넌트가 반환하는 컴포넌트를 '강화된 컴포넌트'라고 한다.

 

React.memo를 이용해 [할 일 관리] 앱 최적화하기

1. React.memo 기본 사용법

  • 인수로 전달한 컴포넌트를 메모이제이션된 컴포넌트로 만들어 반환한다.
    • Props가 메모이제이션의 기준이 된다.
    • 즉, 반환하는 컴포넌트는 부모 컴포넌트에서 전달된 Props가 변경되지 않는 한 리렌더 되지 않는다.
      → 렌더링을 방지하여 성능 최적화에 도움이 된다.
  • 사용 방법
    • 강화하고 싶은(메모이제이션을 적용하고 싶은) 컴포넌트를 React.memo로 감싸면 된다.
const memoizedComp = React.memo(Comp);

Comp : 메모이제이션하려는 컴포넌트

 

  • 컴포넌트를 선언함과 동시에 메모제이션을 선언하기
const CompA = React.memo(() => {
	console.log("컴포넌트가 호출되었습니다.");
	return <div>CompA</div>;
});
  • Props가 많을 때는 판별변수를 인수로 전달해 Props의 특정 값만으로 리렌더 여부를 판단 가능
const Comp = ({a, b, c}) => {
	console.log("컴포넌트가 호출되었습니다.");
	return <div>Comp</div>;
};

function areEqual(prevProps, nextProps) { //판별함수(preProps는 이전 Props값, nextProps는 새롭게 바뀐 Props값)
	if(prevProps.a === nextProps.a) {
		return true; //true를 반환하면 리렌더되지 않음
	} else {
		return false; //false를 반환하면 리렌더됨
	}
}

const MemoizedComp = React.memo(Comp, areEqual); //두번째 인수로 판별함수 전달
//Props의 a가 변경될 때만 리렌더 된다.

 

 

Header 컴포넌트의 리렌더 방지하기

  • Header 컴포넌트는 날짜를 보여주는 역할만 한다.
  • 만약 리렌더 된다면 불필요한 렌더링이다.
    • 새 아이템을 추가할 때 App컴포넌트가 리렌더되어 Header 컴포넌트도 불필요하게 리렌딩 된다.
  • Header 컴포넌트를 내보낼 때 React.memo로 감싸기
import React from "react"; //React 라이브러리 추가

const Header = () => {
    return (
        <div className="Header">
            <h3>오늘은 📆</h3>
            <h1>{new Date().toDateString()}</h1>
        </div>
    );
};

export default React.memo(Header); //메모이제이션을 적용하여 내보내기

 

→ 마운트 할 때만 Header 컴포넌트가 렌더링된다.

 

 

TodoItem 컴포넌트 리렌더 방지하기

  • TodoItem 컴포넌트는 사용자가 등록한 할 일 아이템의 개수만큼 렌더링 된다.
  • TodoItem 컴포넌트는 개별 아이템 체크박스에서 완료/미완료를 토글할 때만 리렌더 필요
    • TodoList 의 자식 컴포넌트이므로 불필요한 리렌더 발생

 

  • React.memo를 이용해 TodoItem 컴포넌트의 리렌더 방지하기
import React from "react";

const TodoItem = ({id, content, isDone, createdDate, onUpdate, onDelete}) => {
   (...)
};

export default React.memo(TodoItem);

 

  • 하지만, 아이템을 추가하면 todo가 업데이트되어 App컴포넌트가 리렌더 되면서 
    TodoItem에 전달되는 Props도 변경되어 리렌더가 발생!
    • onUpdate, onDelete가 App컴포넌트에서 생성되어 TodoItem 컴포넌트 Props으로 전달된다.

 

  • App 컴포넌트를 리렌더 하면 새롭게 만든 onUpdate는 새로운 참조값을 갖게 된다.
  • React.memo는 Props가 변한 것으로 판단한다.
  • 그러므로 다시 렌더링 된다.

→ 이를 방지하기 위해 useCallback 리액트 훅을 사용한다.

 

 

 

불필요한 함수 재생성 방지하기

  • useCallback : 컴포넌트가 리렌더될 때 내부에 작성된 함수를 다시 생성하지 않도록 메모이제이션하는 리액트 훅

 

useCallback을 이용해 [할 일 관리] 앱 최적화하기

1. useCallback의 기본 용법

const memoizedFunc = useCallback(func, deps)

func : 콜백함수
deps : 의존성 배열

 

  • 의존성 배열의 값이 바뀌면 콜백 함수를 다시 만들어 반환한다.
  • 의존성 배열이 빈 배열 : 전달한 콜백 함수를 다시 생성되지 않게 하는 방법

2. useCallback과 함수형 업데이트

  • State 변수에 접근하면서 State값을 계속 업데이트하기 위한 방법
  • 함수형 업데이트 : setState의 인수로 콜백함수를 전달하는 기능
const onCreate = useCallback(() => {
	setState((state) => [newItem, ...state]);
    }, []);
  • 항상 최신 State 값을 매개변수로 저장한다.
  • 콜백 함수가 반환한 값은 새로운 State값이 되어 업데이트 된다.

setState : 최신 State 값을 추적하기 위한 방법

 

 

3. useCallback을 이용해 TodoItem 컴포넌트의 리렌더 방지하기

 

  • onUpdate와 onDelete를 useCallback으로 메모이제이션해 이 함수들을 다시 생성하지 않도록 한다.
    • TodoItem이 불필요한 상황에서 리렌더 되지 않도록 하기
  //App.js
  
  const onUpdate = useCallback((targetId) => { //useCallback으로 수정
    dispatch({
      type: "UPDATE",
      targetId,
    });
  }, []);

  const onDelete = useCallback((targetId) => {
    dispatch({
      type: "DELETE",
      targetId,
    });
  },[]);

 

 

최적화할 때 유의할 점

1.  최적화는 항상 마지막에 하기

2. 모든 것을 최적화할 필요는 없다.

3. 컴포넌트 구조를 잘 설계했는지 다시 한번 돌아보기

4. 최적화는 여기서 끝나지 않는다

 

 

 

 


Quiz 

1. State 값을 변경하는 코드를 (상태 변환 코드)라고 부른다.

2. "상태 변화 코드를 컴포넌트에서 분리한다"는 의미는 상태 변화 코드를 (외부)에 작성한다는 뜻이다.

3. 상태 변화 코드를 컴포넌트 밖으로 분리하기위해 (useReducer) 리액트 훅을 사용한다.

4. dispatch 함수는 인수로 객체를 전달한다 이때 이 객체를 (action 객체)라고 부른다.

5.

const [count, dispatch] = useReducer(reducer, 0)


dispatch함수 호출 → (reducer) 함수 호출 → (count) 값 업데이트

 

6. 실제 상태 변화는 (

reducer)

함수에서 일어난다.

 

프로그래밍 문제

1. reducer을 상태 변화 함수로 가지고, mockTodo를 초깃값으로 가지는 useReducer을 작성해라.

(상태 변화 촉발 함수 : dispatch, state값 : count)

const mockTodo = [
    {
      id:0,
      isDone: false,
      content: "React 공부하기",
      createdDate: new Date().getTime(),
    },
    {
      id:1,
      isDone: false,
      content: "Spring 공부하기",
      createdDate: new Date().getTime(),
    },
    {
      id:2,
      isDone: false,
      content: "Django 공부하기",
      createdDate: new Date().getTime(),
    },

  ];

 

 

2. onCreate 함수를 참고하여, action.type이 "CREATE"일 때 reducer 함수를 작성하여라.

  const onCreate = (content) => {
    dispatch({
      type: "CREATE",
      newItem: {
        id: idRef.current,
        content,
        isDone: false,
        createdDate: new Date().getTime(),
      },
    });
    idRef.current += 1;
  };
function reducer(state, action) {
  switch(action.type){
    case "CREATE":
     //이 부분을 채우면 됨.
     //추가된 아이템 + 원래 state
    
    default:
      return state;
  }
}

 

 


1.

const [count, dispatch] = useReducer(reducer, mockTodo)

 

 

2. 

function reducer(state, action) {
  switch(action.type){
    case "CREATE":
      return [action.newItem, ...state];
    default:
      return state;
  }
}

 

 

 

출처 : 이정환, 『한 입 크기로 잘라먹는 리액트』, 프로그래밍인사이트(2023), p344-383.

Corner React.js 3 

Editor: smurfs

728x90

관련글 더보기