[React] 11. Context

DonaDona·2024년 10월 14일

11.1) Context란

React Context

  • 컴포넌트간의 데이터를 전달하는 또 다른 방법
  • 기존의 Props가 가지고 있던 단점을 해결할 수 있음

Props 단점 - Props Drilling

  • 부모 -> 자식으로만 데이터를 전달할 수 있었음
  • App -> List -> TodoItem 2중으로 전달해야하는 구조

  • Context에 함수를 보관해놓으면 Props를 이용하지 않고 다이렉트로 컨텍스트를 통해 필요한 데이터를 공급해 줄 수 있음
  • 여러개의 Context를 만드는 것도 가능

11.2) Context 사용하기

  • 컨텍스트는 컴포넌트 외부에 선언. 컴포넌트 리렌더링 될 때 다시 생성되지 않게 하기 위함

Provider

  • Context에서 제대로 알아야하는 프로퍼티

App.jsx

import {
  useState,
  useRef,
  useReducer,
  useCallback,
  createContext,
} from "react";
import "./App.css";
import Header from "./components/Header";
import Editor from "./components/Editor";
import List from "./components/List";

const mockData = [
  {
    id: 0,
    isDone: false,
    content: "React 공부하기",
    date: new Date().getTime(),
  },
  {
    id: 1,
    isDone: false,
    content: "빨래하기",
    date: new Date().getTime(),
  },
  {
    id: 2,
    isDone: false,
    content: "노래연습하기",
    date: new Date().getTime(),
  },
];

function reducer(state, action) {
  switch (action.type) {
    case "CREATE":
      return [action.data, ...state];
    case "UPDATE":
      return state.map((item) =>
        item.id === action.targetId ? { ...item, isDone: !item.isDone } : item
      );
    case "DELETE":
      return state.filter((item) => item.id !== action.targetId);
    default:
      return state;
  }
}

export const TodoContext = createContext();

function App() {
  const [todos, dispatch] = useReducer(reducer, mockData);
  const idRef = useRef(3);

  const onCreate = useCallback((content) => {
    dispatch({
      type: "CREATE",
      data: {
        id: idRef.current++,
        isDone: false,
        content: content,
        date: new Date().getTime(),
      },
    });
  }, []);

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

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

  return (
    <div className="App">
      <Header />
      <TodoContext.Provider
        value={{
          todos,
          onCreate,
          onUpdate,
          onDelete,
        }}
      >
        <Editor />
        <List />
      </TodoContext.Provider>
    </div>
  );
}

export default App;
  • Provider 컴포넌트의 모든 자식 또는 모든 자손 컴포넌트들은 Value Props로 설정한 컨텍스트가 공급하는 값들을 다이렉트로 꺼내와서 언제든지 사용할 수 있는 상태가 됨


11.3) Context 분리하기

Context 적용 후

  • 지난시간 - Context적용으로 props drilling 해결, 최적화가 풀림
  • todos 스테이트가 변경이 되어서 App 컴포넌트가 리렌더링이 될 때 Provider 컴포넌트에게 Value Props로 전달하는 객체 자체가 다시 생성된다.

해결법


App.jsx

function App() {
  // 재생성 방지
  const memoizedDispatch = useMemo(() => {
  return { onCreate, onUpdate, onDelete };
  }, []);

  return (
    <div className="App">
      <Header />
      <TodoStateContext.Provider value={todos}>
        <TodoDispatchContext.Provider value={memoizedDispatch}>
          <Editor />
          <List />
        </TodoDispatchContext.Provider>
      </TodoStateContext.Provider>
    </div>
  );
}

export default App;

List.jsx

import { useState, useMemo, useContext } from "react";
import { TodoStateContext } from "../App";

const List = () => {
  const todos = useContext(TodoStateContext);
  ...
}

Editor.jsx

import { useState, useRef, useContext } from "react";
import { TodoDispatchContext } from "../App";

const Editor = () => {
  const { onCreate } = useContext(TodoDispatchContext);
  ...
}
profile
기록용 공부용 개발 블로그

0개의 댓글