TroubleShooting

[포켓몬 도감] - Context API로 바꾸기

집으로 감자 2025. 2. 4. 11:43

문제 상황

요구 사항

props drilling으로 작성한 포켓몬 도감 코드를 Context API로 변환하세요

 

다음 요구사항을 만족하기 위해서는 useContext 훅을 사용해야했다.

useContext를 이용해서 state를 전역에서 관리하는 것을 배우긴 했지만,

🚨 함수를 전역에서 관리하는 것을 알지는 못했다.

 

검색을 통해 Provider을 새로 만들어 줄 수 있는 방법이 있다는 것을 알게 됐다.

 

 

해결 과정

Provider 새로 만드는 방법

import { createContext } from 'react';

const CounterContext = createContext();

function CounterProvider({ children }) {
  return <CounterContext.Provider>{children}</CounterContext.Provider>;
}

function App() {
  return (
    <CounterProvider>
      <div>
        <Value />
        <Buttons />
      </div>
    </CounterProvider>
  );
}

다음과 같은 구조로 Provider을 새로 만들 수 있다.

✅ 여기 안에서 state와 함수를 관리하면 된다.

 

import { createContext } from 'react';

const CounterContext = createContext();

function CounterProvider({ children }) {
  const value = {name, age}
  return <CounterContext.Provider value={value}>{children}</CounterContext.Provider>;
}

function App() {
  return (
    <CounterProvider>
      <div>
        <Value />
        <Buttons />
      </div>
    </CounterProvider>
  );
}

Provider 안에 미리 value를 전달할 수도 있다. 

 

포켓몬 도감에 Provider 적용하기

01 PokemonContext 생성

import { createContext, useEffect, useState } from "react";

export const PokemonContext = createContext(null);

export const PokemonProvider = ({ children }) => {};

createContext를 이용해서 PokemonContext를 생성한다.

PokemonProvider 함수를 선언한 뒤, 인자로 children을 전달한다.

 

02 전역에서 관리할 state와 함수 코드 작성

import { createContext, useEffect, useState } from "react";

export const PokemonContext = createContext(null);

export const PokemonProvider = ({ children }) => {
  const [pokemonList, setPokemonList] = useState(
    JSON.parse(localStorage.getItem("pokemonList")) || []
  );

  /** 로컬 스토리지 저장 */
  useEffect(() => {
    localStorage.setItem("pokemonList", JSON.stringify(pokemonList));
  }, [pokemonList]);

  const addPokemon = (id, img_url, korean_name, types) => {
    /** 예외상황01: 이미 등록된 포켓몬일 때 */
    if (pokemonList.some((pokemon) => pokemon.id === id)) {
      alert("이미 등록된 포켓몬입니다");
      return;
    }
    /** 예외상황02: 포켓몬 6마리 모두 등록됐을 때 */
    if (pokemonList.length > 5) {
      alert("포켓몬 6마리를 모두 등록하셨습니다");
      return;
    }

    setPokemonList((prev) => {
      return [...prev, { id, img_url, korean_name, types }];
    });
  };

  const removePokemon = (id) => {
    setPokemonList((prev) => prev.filter((pokemon) => pokemon.id !== id));
  };
};

내부에는 기존에 작성했던

사용자가 선택한 6마리의 포켓몬 배열의 state
포켓몬 카드 추가 함수 addPokemon
포켓몬 카드 삭제 함수 removePokemon

코드를 넣어준다.

 

03 Provider 컴포넌트 반환하기

const value = {
  pokemonList,
  addPokemon,
  removePokemon,
};

return (
  <PokemonContext.Provider value={value}>{children}</PokemonContext.Provider>
);

마지막으로 Provider에 전달할 value를 미리 작성한 뒤에

Provider 컴포넌트를 반환하면 된다.

 

04 Dex.jsx에 Provider 적용하기

import React, { useEffect, useState } from "react";
import Dashboard from "../components/Dashboard";
import styled from "styled-components";
import PokemonList from "../components/PokemonList";
import Header from "../components/Header";
import { PokemonProvider } from "../context/PokemonContext";

const Dex = () => {
  /** UI */
  return (
    <PokemonProvider>
      <Div>
        <Header />
        <Container>
          <Dashboard />
          <PokemonList />
        </Container>
      </Div>
    </PokemonProvider>
  );
};

export default Dex;

가장 바깥쪽에 <PokemProvider>으로 감싸주면 끝이다.

 

 

 

💻 전체 코드

import { createContext, useEffect, useState } from "react";

export const PokemonContext = createContext(null);

export const PokemonProvider = ({ children }) => {
  const [pokemonList, setPokemonList] = useState(
    JSON.parse(localStorage.getItem("pokemonList")) || []
  );

  /** 로컬 스토리지 저장 */
  useEffect(() => {
    localStorage.setItem("pokemonList", JSON.stringify(pokemonList));
  }, [pokemonList]);

  /**
   * 포켓몬 카드 추가 함수
   * @param {*} id
   * @param {*} img_url
   * @param {*} korean_name
   * @param {*} types
   */
  const addPokemon = (id, img_url, korean_name, types) => {
    /** 예외상황01: 이미 등록된 포켓몬일 때 */
    if (pokemonList.some((pokemon) => pokemon.id === id)) {
      alert("이미 등록된 포켓몬입니다");
      return;
    }
    /** 예외상황02: 포켓몬 6마리 모두 등록됐을 때 */
    if (pokemonList.length > 5) {
      alert("포켓몬 6마리를 모두 등록하셨습니다");
      return;
    }

    setPokemonList((prev) => {
      return [...prev, { id, img_url, korean_name, types }];
    });
  };

  /**
   * 포켓몬 카드 삭제 함수
   * @param {*} id
   */
  const removePokemon = (id) => {
    setPokemonList((prev) => prev.filter((pokemon) => pokemon.id !== id));
  };

  const value = {
    pokemonList,
    addPokemon,
    removePokemon,
  };

  return (
    <PokemonContext.Provider value={value}>{children}</PokemonContext.Provider>
  );
};

 

 

결과

context API를 이용해서 코드를 변경해도 잘 작동하는 것을 확인할 수 있다.

context API를 이용해서 state와 함수를 한곳에서 관리하니 나머지 코드들이 간결해졌다.

변수나 로직을 수정하려고 할 때 모든 파일을 돌아다니면서 수정할 필요가 없어서 굉장히 편하다.

 


[참고자료]

https://velog.io/@velopert/react-context-tutorial

 

 

반응형