[React] 프로젝트 기초공사 2

이재훈·2023년 6월 14일
0

React

목록 보기
24/27

기초공사 항목

  1. 상태관리 세팅하기
    프로젝트 전반적으로 사용될 일기 데이터 State 관리 로직 작성하기

  2. 프로젝트 State Context 세팅하기
    일기 데이터 State를 공급할 Context를 생성하고 Provider로 공급하기

  3. 프로젝트 Dispatch Context 세팅하기
    일기 데이터 State의 Dispatch 함수들을 공급할 Context를 생성하고 Provider로 공급하기

1. 상태관리 세팅하기


현재 만드는 프로젝트의 컴포넌트 구성입니다. 먼저 App 컴포넌트에 일기 데이터 State를 만들고
관리할 수 있는 기능을 만들어 보도록 하겠습니다.

App.js

import "./App.css";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "./pages/Home";
import Edit from "./pages/Edit";
import New from "./pages/New";
import Diary from "./pages/Diary";

function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <Routes>
          <Route path="/" element={<Home />}></Route>
          <Route path="/new" element={<New />}></Route>
          <Route path="/edit" element={<Edit />}></Route>
          <Route path="/diary/:id" element={<Diary />}></Route>
        </Routes>
      </div>
    </BrowserRouter>
  );
}

export default App;

전 게시글에도 정리가 되어있지만 다시 리마인드 해보면서 useReducer를 사용하여 일기 데이터 State 관리 로직을 작성해보겠습니다.

const [data, dispatch] = useReducer(reducer, []);
  • data : 상태 객체
  • dispatch : dispatch 함수 (컴포넌트 내 상태 변경을 할 때 사용 reducer 함수에 넘길 action 객체를 받음.) action 객체에는 type 속성과 해당 행동과 관련된 데이터를 담고 있습니다.
  • reducer : reducer 함수 (함수에 넘길 객체)
  • [] : 초기 상태
    컴포넌트에서 dispatch 함수에 action을 던지면, reducer 함수가 이 action에 따라 state를 변경해 줍니다.

컴포넌트 바깥에 reducer 함수를 선언해 줍니다.

const reducer = (state, action) => {
  let newState = [];
  switch (action.type) {
    case "INIT": {
      return action.data;
    }
    case "CREATE": {
      newState = [action.data, ...state];
      break;
    }
    case "EDIT": {
      state.map((it) => (it.id === action.data.id ? { ...action.data } : it));
      break;
    }
    case "REMOVE": {
      newState = state.filter((it) => it.id !== action.targetId);
      break;
    }
    default:
      return state;
  }
  return newState;
};

reducer 함수는 첫반째 인자로 현재 state의 상태, 두번째 인자로 type과 기타 행동에 필요한 데이터들이 넘어옵니다.

이제 컴포넌트 안에서 해당 dispatch를 호출해보도록 하겠습니다.

function App() {
  const [data, dispatch] = useReducer(reducer, []);

  const dataId = useRef(0);

  const onCreate = (date, content, emotion) => {
    dispatch({
      type: "CREATE",
      data: {
        id: dataId.current,
        date: new Date(date).getTime(),
        content,
        emotion,
      },
    });
  };

  const onRemove = (targetId) => {
    dispatch({
      type: "REMOVE",
      targetId,
    });
  };

  const onEdit = (targetId, date, content, emotion) => {
    dispatch({
      type: "EDIT",
      data: {
        id: targetId,
        date: new Date(date).getTime,
        content,
        emotion,
      },
    });
  };

이제 react context를 사용하여 하위 컴포넌트 어디서든 사용 가능하게 하도록 하겠습니다.
app 컴포넌트 바깥에 선언하도록 합니다.

export const DiaryStateContext = React.createContext();
export const DiaryDispatchContext = React.createContext();

//... 생략
  return (
    <DiaryStateContext value={data}>
      <DiaryDispatchContext value={(onCreate, onEdit, onRemove)}>
        <BrowserRouter>
          <div className="App">
            <Routes>
              <Route path="/" element={<Home />}></Route>
              <Route path="/new" element={<New />}></Route>
              <Route path="/edit" element={<Edit />}></Route>
              <Route path="/diary/:id" element={<Diary />}></Route>
            </Routes>
          </div>
        </BrowserRouter>
      </DiaryDispatchContext>
    </DiaryStateContext>
  );

선언을 해주고 value에 data와 onCreate 등의 함수를 전달하였습니다. 최적화는 일단 신경쓰지 않도록 하겠습니다.

App.js

import "./App.css";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "./pages/Home";
import Edit from "./pages/Edit";
import New from "./pages/New";
import Diary from "./pages/Diary";
import React, { useReducer, useRef } from "react";

const reducer = (state, action) => {
  let newState = [];

  switch (action.type) {
    case "INIT": {
      return action.data;
    }
    case "CREATE": {
      newState = [action.data, ...state];
      break;
    }
    case "REMOVE": {
      newState = state.filter((it) => it.id !== action.targetId);
      break;
    }
    case "EDIT": {
      newState = state.map((it) =>
        it.id === action.data.id ? { ...action.data } : it
      );
      break;
    }
    default: {
      return state;
    }
  }
  return newState;
};

export const DiaryStateContext = React.createContext();
export const DiaryDispatchContext = React.createContext();

function App() {
  const [data, dispatch] = useReducer(reducer, []);

  const dataId = useRef(0);

  const onCreate = (date, content, emotion) => {
    dispatch({
      type: "CREATE",
      data: {
        id: dataId.current,
        date: new Date(date).getTime(),
        content,
        emotion,
      },
    });
  };

  const onRemove = (targetId) => {
    dispatch({
      type: "REMOVE",
      targetId,
    });
  };

  const onEdit = (targetId, date, content, emotion) => {
    dispatch({
      type: "EDIT",
      data: {
        id: targetId,
        date: new Date(date).getTime,
        content,
        emotion,
      },
    });
  };

  return (
    <DiaryStateContext value={data}>
      <DiaryDispatchContext value={(onCreate, onEdit, onRemove)}>
        <BrowserRouter>
          <div className="App">
            <Routes>
              <Route path="/" element={<Home />}></Route>
              <Route path="/new" element={<New />}></Route>
              <Route path="/edit" element={<Edit />}></Route>
              <Route path="/diary/:id" element={<Diary />}></Route>
            </Routes>
          </div>
        </BrowserRouter>
      </DiaryDispatchContext>
    </DiaryStateContext>
  );
}

export default App;

리액트 공식 홈페이지
https://ko.legacy.reactjs.org/docs/react-api.html#reactmemo
해당 게시글은 인프런 강의
"한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지(이정환)"
를 정리한 내용입니다. 쉽게 잘 설명해주시니 여러분도 강의를 듣는 것을 추천드립니다.

redux 패턴 참조
https://www.daleseo.com/react-hooks-use-reducer/

profile
부족함을 인정하고 노력하자

0개의 댓글