리액트 같은 Single Page Application(html 페이지가 하나인 사이트)의 단점은 컴포너튼간의 state 공유가 어려운 것이다. 즉, 컴포넌트에 state를 자식의 자식 컴포넌트(자손 컴포넌트)에 사용하고 싶으면 props를 2번 전송해 주어야 한다.
App 컴포넌트에 있는 shoes라는 state를 TabContent 컴포넌트에서 사용하고 싶으면 어떻게 해야 할까?
App → Detail → TabContent 이렇게 props를 2번 전송해야 한다.
(App.js)
function App() {
let [shoes, setShoes] = useState(data);
return (
(생략)
<Route path="/detail/:id" element={<Detail shoes={shoes} />} />
);
}
(Detail.js)
function Detail(props){
return (
(생략)
<TabContent shoes={props.shoes}/>
)
}
function TabContent(props){
return (
<div>
{shoes[0].title}}
</div>
)
}
export default Detail
이렇게 props를 여러번 전송해 주는 방법 대신 리액트의 기본 문법인 Context API 문법을 사용하거나 Redux 같은 외부 라이브러리를 사용하면 된다.
Contex API 문법으로 props 없이 state 공유하기
stock이라는 state를 App 컴포넌트에 만들고, 이것을 TabContent라는 자식 컴포넌트에 쓰고 싶다고 가정해 보자.
1. createContext()함수를 가져와서 context를 하나 만들기
(App.js)
import { createContext, useState } from 'react';
export let Context1 = createContext();
function App() {
let [stock, setStock] = useState([10, 11, 12]);
(생략)
}
Context를 쉽게 비유해서 설명하자면 state 보관함이다.
2. <Context>로 원하는 컴포넌트 감싸고 공유 원하는 state를 value 안에 작성
(App.js)
import { createContext, useState } from 'react';
export let Context1 = createContext();
function App() {
let [stock, setStock] = useState([10, 11, 12]);
return (
(생략)
<Route path="/detail/:id" element={
<Context1.Provider value={{stock}}>
<Detail shoes={shoes} />
</Context1.Provider>
} />
}
Context1로 원하는 곳을 감싸고 공유 원하는 state를 value 안에 작성한다.
value={{state1, state2, ...}}
그러면 Context1로 감싼 모든 컴포넌트와 그 자식 컴포넌트는 state를 props 전송 없이 직정 사용 가능하다.
Context 안에 있던 state 사용하기
1. 만들어 둔 Context를 import하기
(Detail.js)
import {Context1} from './../App.js';
2. useContext() 안에 넣기
그러면 이제 그 자리에 공유했던 state가 전부 남는다.
(Detail.js)
import {useContext} from 'react';
import {Context1} from './../App.js';
function Detail(){
let {stock} = useContext(Context1);
return (
<div> {stock} </div>
)
}
▲ useContext는 Context를 해제해 주는 함수.
그러면 그 자리에 공유했던 모든 state가 남는다. 변수에 담아서 가져다 사용하면 된다.
Detail 안에 있는 모든 자식 컴포넌트도 useContext() 사용하면 자유롭게 stock이라는 state 사용이 가능하다.
(Detail.js)
function TabContent(){
let {stock} = useContext(Context1);
return (
<div>
{stock[0]}
</div>
)
}
Context API 단점
실전에 잘 사용하지 않는 이유는
1. state 변경시 쓸데없는 컴포넌트까지 전부 재렌더링됨
2. useContext()를 쓰고 있는 컴포넌트는 나중에 다른 파일에서 재사용할 때 Context를 import 해야 하는 불편함
즉, 성능 이슈가 생기고 컴포넌트 재활용이 어렵다.
그래서 Context API보다 redux 같은 외부 라이브러리들을 많이 사용한다.
* 이 포스팅은 코딩애플 강의를 토대로 작성하였습니다.