■ Context API
Context 는 그 하위에 있는 요소들끼리 공유하는 맥락으로 어디서 사용되는가에 따라 부분적 전역상태로 사용 될 수 있음
* Context !== 전역상태
* 단순한 데이터 전달과 같은 경우 props 사용이 더 적절하다.
⚬ createContext : 컨텍스트를 만듦
⚬ Context Provider : 컨텍스트를 원하는 위치에 주입
⚬ useContext : 컨텍스트를 사용
- 사용할 Context 는 export 가 되어있어야 함!
// createContext
import { createContext } from "react";
// 파스칼케이스(대문자로 작성), 사용할 컨텍스트 export 해줄것!
export const CountContext = createContext();
// Context Provider
// 생성한 컨텍스트에 Provider를 붙여서 사용, 하위 컴포넌트에 전달할 value 추가
return (
<div>
<CountContext.Provider value={{ count, setCount }}>
{count}
<InputArea />
<ListArea />
</CountContext.Provider>
</div>
);
};
// useContext
// 자식 컴포넌트에서 useContext hook을 사용하여 Context에 넣은 값에 접근할 수 있다.
import { useContext } from "react";
import { CountContext } from "../App";
const SubInputTagArea = () => {
// 구조분해할당으로 필요한것만 꺼내서 사용할 수 있음
// useContext 인자로 사용할 컨텍스트 넣고 import 하기
const { setCount } = useContext(CountContext);
return (
<div>
<button onClick={() => setCount((prev) => prev + 1)}>증가</button>
</div>
);
};
■ Context API로 리팩터링하기
// Dex.jsx
// PokemonContext 생성
export const PokemonContext = createContext();
⠇
// PokemonContext.Provider 로 하위 컴포넌트를 감싸서 value로 값 전달
return (
<>
<PokemonContext.Provider
value={{ selectedPokemon, removePokemon, addPokemon, MOCK_DATA }}
>
<Dashboard />
<PokemonList />
</PokemonContext.Provider>
</>
);
⚬ 자식 컴포넌트인 PokemonCard로 props 전달 역할만 하던 PokemonList 컴포넌트
// PokemonList.jsx
const PokemonList = () => {
return (
<StListContainer>
<PokemonCard />
</StListContainer>
);
⚬ Dashboard 와 pokemonCard 컴포넌트에서는 props 로 전달받은 값들 제거 후 useContext(PokemonContext) 로 필요한 값 사용해서 기존 코드를 리팩토링 하였다.
// Dashboard.jsx
const Dashboard = () => {
const { selectedPokemon, removePokemon } = useContext(PokemonContext);
const navigate = useNavigate();
return (
<StDashboardContainer>
<h2>My Pokemon</h2>
<StPokemonContainer>
{selectedPokemon.map((pokemon) => {
return (
<StCard
key={pokemon.id}
onClick={() => navigate(`/pokemon-detail?id=${pokemon.id}`)}
>
<img src={pokemon.img} alt={`${pokemon.name} 이미지`} />
<p>{pokemon.name}</p>
<p>{pokemon.number}</p>
{pokemon.isSelected ? (
<StButton
onClick={(e) => {
e.stopPropagation();
removePokemon(pokemon);
}}
>
삭제
</StButton>
) : (
<StButton>추가</StButton>
)}
</StCard>
);
})}
</StPokemonContainer>
</StDashboardContainer>
);
};
// PokemonCard.jsx
const PokemonCard = () => {
const { MOCK_DATA, addPokemon } = useContext(PokemonContext);
const navigate = useNavigate();
let isSelected = true;
return (
<>
{MOCK_DATA.map((pokemon) => {
return (
<StCard
key={pokemon.id}
onClick={() => navigate(`/pokemon-detail?id=${pokemon.id}`)}
>
<div>
<img
src={pokemon.img_url}
alt={`${pokemon.korean_name} 이미지`}
/>
<p>{pokemon.korean_name}</p>
<p>No. {pokemon.id} </p>
</div>
{isSelected ? (
<StButton
onClick={(e) => {
e.stopPropagation();
addPokemon(pokemon);
}}
>
추가
</StButton>
) : (
<StButton>삭제</StButton>
)}
</StCard>
);
})}
</>
);
};
🤔 느낀점
어제 과제의 전반적인 기능을 만들고 prop drilling을 사용해서 상태관리를 하던 부분을 Context API 로 리팩토링 했다.
강의에서는 조금 간단한 예제를 통해서 학습했었고 내가 직접 과제에 적용할 때는 익숙하지 않아서 어떻게 적용해야할 지 감이 안왔었다.
게다가 이미 내가 작성했던 여러 코드들 때문에 더 복잡하게 느껴졌다.
오늘 실시간 강의와 보충수업에서 한번 더 다루었던 Context API 내용을 토대로 과제에 적용해보았는데 계속 오류가 나서 코드를 쓰고 되돌리기를 여러번 반복했다. 그 과정에서 오류메세지를 하나씩 해결하다보니 더이상 오류가 발생하지 않았다.
혼자 몇시간동안 이리저리 삽질하다보니 사용방법이 이전보다 훨씬 익숙해지게 된 것 같다. 사실 Context를 새로운 파일을 생성해서 사용하고 싶었는데 consumer, {children}... 등 관련 개념부터 이해가 부족해서 우선 배웠던 방법으로 해보았다. 조금 아쉬워서 좀더 공부해서 외부 파일로 Context를 생성해서 사용하는 방법을 연구해봐야겠다.
'TIL' 카테고리의 다른 글
개인과제 - 포켓몬도감 만들기 ④ (0) | 2024.08.27 |
---|---|
개인과제 - 포켓몬도감 만들기 ③ (0) | 2024.08.26 |
개인과제 - 포켓몬도감 만들기 ① (2) | 2024.08.22 |
react-router-dom (0) | 2024.08.21 |
RTK, RRD, Supabase (0) | 2024.08.20 |