Context API리액트 심화/Context API2022. 6. 6. 13:22
Table of Contents
공식문서
https://ko.reactjs.org/docs/context.html#caveats
Context API?
- react는 16.3 버전부터 정식적으로 Context API를 지원하고 있다. 일반적으로 부모와 자식간 props를 날려 state를 변화시키는 것과는 달리 context api는 컴포넌트 간 간격이 없다.
- context를 이용하면 단계마다 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공할 수 있다.
- 즉, 컴포넌트를 건너띄고 다른 컴포넌트에서 state, function을 사용할 수 있다. 또한 redux의 많은 어려운 개념보다 context API는 Provider, Consumer, createContext 개념만 알면 적용이 가능하다
- 변경이 잦게 일어나는 전역 데이터를 사용할 때는 사실 그다지 적합하진 않다. 작은 데이터 조각을 여기저기서 사용하고 싶을 땐 제법 효율이 좋다.
사용법
- 데이터를 저장할 공간을 만들어서 가져다 쓰기 위해 Provider로 데이터를 사용할 전역 컴포넌트(App.js나 index.js)를 감싸서 데이터를 주입하고 Provider에서 가지고 있는 전체 데이터를 Consumer(주입한 데이터를 구독하게 하는 역할)로 한번 더 감싼 다음 그 안에 컴포넌트를 넣는다.
- Consumer 없이 useContext라는 훅으로 데이터를 구독할 수 있다.
- 대략적인 구조는 아래와 같다
<Provider>
<Consumer>
<Component1/>
<Component2/>
</Consumer>
</Provider>
1. 저장소 만들기 createContext()
- createContext()로 contextAPI를 생성한다.
- createContext()인자로 default값을 넣을 수 있는데 만약 Consumer가 Provider로 감싸져 있지 않다면 해당 디폴트 값을 쓸 수 있다.
import React from "react";
// 자식이 context 하기 위해 export
export const store = React.createContext();
const App = () => {
return (
)
}
export default App;
2. 데이터 주입하기 <Provider>
- <store이름.Provider value={전역 상태}>로 데이터를 주입!
- 반드시 value를 꼭 명시해야 한다!
import React from "react";
export const store = React.createContext();
const App = () => {
return (
<store.Provider value={{name:"duck", height: 180}}>
</store.Provider>
)
}
export default App;
3-1. 데이터 구독하기 <Consumer>
- 데이터를 구독할 컴포넌트에서 store를 불러와서 <Consumer>로 감싼 후 { }안에 함수를 작성해서 Provider에서 보낸 value를 인자로 넣어서 사용할 수 있다
App.js
import React from "react";
import Component from "./Component";
// store를 export해서 자식에서 사용할 수 있게 한다!
export const store = React.createContext("default");
const App = () => {
return (
<store.Provider value={{name:"duck", height: 180}}>
<Component></Component>
</store.Provider>
)
}
export default App;
Component.js
// context를 가져옴
import { store } from "./App";
const Component = () => {
return(
<store.Consumer>
{(value) => (<div>{value.name}</div>)}
</store.Consumer>
);
}
export default Component;
주의사항 - Provider가 없는 Consumer
- 만약, Provider로 감싸지지 않은 컴포넌트가 Consumer로 store를 구독하면 Provider에서 넘겨준 디폴트 값이 넘어간다!!
App.js
import React from "react";
import Component from "./Component";
export const store = React.createContext("default");
const App = () => {
return (
<Component></Component>
)
}
export default App;
Component.js
// context를 가져옴
import { store } from "./App";
const Component = () => {
return(
<store.Consumer>
{(value) => (<div>{value}</div>)}
</store.Consumer>
);
}
export default Component;
3-2. 데이터 구독하기 useContext()
- useContext() 훅을 이용해서 store를 구독할 수 있다!
App.js
import React from "react";
import Component from "./Component";
// store를 export해서 자식에서 사용할 수 있게 한다!
export const store = React.createContext("default");
const App = () => {
return (
<store.Provider value={{name:"duck", height: 180}}>
<Component></Component>
</store.Provider>
)
}
export default App;
Component.js
// context를 가져옴
import { store } from "./App";
import { useContext } from "react";
const Component = () => {
// useContext로 store의 데이터 구독!
const data = useContext(store);
console.log(data)
return(
<>
<div>name: {data.name}</div>
<div>height: {data.height}</div>
</>
);
}
export default Component;
주의사항 - Provider가 없는 Consumer
- 만약, Provider로 감싸지지 않은 컴포넌트가 Consumer로 store를 구독하면 Provider에서 넘겨준 디폴트 값이 넘어간다!!
App.js
import React from "react";
import Component from "./Component";
// store를 export해서 자식에서 사용할 수 있게 한다!
export const store = React.createContext("default");
const App = () => {
return (
<Component></Component>
)
}
export default App;
Component.js
// context를 가져옴
import { store } from "./App";
import { useContext } from "react";
const Component = () => {
const data = useContext(store);
console.log(data)
return(
<>
<div>{data}</div>
</>
);
}
export default Component;
4. 데이터 변경하기 - useState() 사용
- Provider에 단순히 데이터 뿐만 아니라 함수도 넘길 수 있다!
- App.js에서 useState를 이용해서 state와 state를 변경하는 함수를 Provider의 value에 넘겨서 자식 컴포넌트들이 구독해서 데이터를 변경할 수 있다!
- 아래 예시는 App.js의 state와 state 변경 함수를 Provider의 value에 넘겨서 자식 컴포넌트가 state와 state 변경 함수를 받아와서 App.js의 state 값을 변경시키는 예시이다!
App.js
import React from "react";
import { useState } from "react";
import Component from "./Component";
// store를 export해서 자식에서 사용할 수 있게 한다!
export const store = React.createContext("default");
const App = () => {
// Provider의 value로 넘겨줄 state와 state 변경 함수
const [data, setData] = useState({name:"duck", height: 180})
return (
<store.Provider value={[data, setData]}>
<Component></Component>
</store.Provider>
)
}
export default App;
Component.js
// context를 가져옴
import { store } from "./App";
import { useContext } from "react";
const Component = () => {
// Provider에서 받은 App.js의 state와 state변경 함수
const [data, setData] = useContext(store);
const add_height = () => {
// state의 height 값이 1씩 증가!
setData({...data, height: data.height+1})
}
console.log(data)
return(
<>
<div>name: {data.name}</div>
<div>name: {data.height}</div>
<button onClick={add_height}>키 늘리기</button>
</>
);
}
export default Component;
렌더링 이슈
- Provider의 value 값이 변하면 Provider 하위의 context를 구독하지 않은 자식들까지 렌더링이 발생한다!!
- 아래 예시를 살펴보자.
- GrandChildComponent는 useContext를 사용해서 Proivder의 value가 변경이 되면 리렌더링이 일어나야겠지만 ChildComponent는 context를 구독하지 않고 있기 때문에 리렌더링이 필요하지 않다!
import React from "react";
import { useState, useContext } from "react";
// store를 export해서 자식에서 사용할 수 있게 한다!
export const store = React.createContext("default");
const Component = () => {
// Provider에서 받은 App.js의 state와 state변경 함수
const [data, setData] = useContext(store);
const add_height = () => {
// state의 height 값이 1씩 증가!
setData({...data, height: data.height+1})
}
return(
<>
<div>name: {data.name}</div>
<div>name: {data.height}</div>
<button onClick={add_height}>키 늘리기</button>
<ChildComponent/>
</>
);
}
// context를 구독하지 않는 자식이 리렌더링 발생
const ChildComponent = (props) => {
console.log("ChildComponent")
return(<GrandChildComponent/>)
}
// memo로 부모가 리렌더링이 방지되어 있지만 useContext로 store를 구독하기 때문에
// 렌더링이 일어남!
const GrandChildComponent = () => {
const [data, setData] = useContext(store)
console.log("GrandChildComponent")
return(null)
}
const App = () => {
// Provider의 value로 넘겨줄 state와 state 변경 함수
const [data, setData] = useState({name:"duck", height: 180})
return (
<store.Provider value={[data, setData]}>
<Component></Component>
</store.Provider>
)
}
export default App;
- Provider 하위에 있지만, context를 사용하지 않는 ChildComponent까지 리렌더링이 일어난다!
- 이러한 불필요한 리렌더링을 방지하기 위해 Context를 구독하지 않는 자식들은 React.memo를 사용해서 리렌더링을 방지하자!!
import React from "react";
import { useState, useContext } from "react";
// store를 export해서 자식에서 사용할 수 있게 한다!
export const store = React.createContext("default");
const Component = () => {
// Provider에서 받은 App.js의 state와 state변경 함수
const [data, setData] = useContext(store);
const add_height = () => {
// state의 height 값이 1씩 증가!
setData({...data, height: data.height+1})
}
return(
<>
<div>name: {data.name}</div>
<div>name: {data.height}</div>
<button onClick={add_height}>키 늘리기</button>
<ChildComponent/>
</>
);
}
// context를 사용하지 않는 컴포넌트에 memo를 사용해서 리렌더링 방지!
const ChildComponent = React.memo((props) => {
console.log("ChildComponent")
return(<GrandChildComponent/>)
})
// memo로 부모가 리렌더링이 방지되어 있지만 useContext로 store를 구독하기 때문에
// 리렌더링이 일어남
const GrandChildComponent = () => {
const [data, setData] = useContext(store)
console.log("GrandChildComponent")
return(null)
}
const App = () => {
// Provider의 value로 넘겨줄 state와 state 변경 함수
const [data, setData] = useState({name:"duck", height: 180})
return (
<store.Provider value={[data, setData]}>
<Component></Component>
</store.Provider>
)
}
export default App;
@덕구공 :: Duck9s'
주니어 개발자에욤
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!