전역적인 상태 관리를 위한 기능
React를 이용하여 개발 시, 부모 컴포넌트에서 자식 컴포넌트로 데이터를 보낼 때, props
를 사용한다.
하지만 개발 중인 애플리케이션 규모가 매우 커서 페이지 개수가 많아 컴포넌트의 깊이(depth)가 깊을 때, 전달해야 할 props의 개수가 많을 때, props만 이용하기에는 매우 부담이 된다.
그래서 값을 전역적으로 관리할 수 있는 context API, 상태 관리 라이브러리(Redux, MobX 등)를 많이 사용하는데, 이번에는 context API에 관한 내용을 공부하였다.
위에서 언급했듯이, 컴포넌트의 깊이가 깊을 때 곤욕을 치를 수 있다.
위의 그림 처럼 A 컴포넌트는 c, d, B 하위 컴포넌트를 가지고 있는데, A에서 B로 data 변수를 props로 보내주려면 c와 d를 거쳐서 B로 전달하게 된다.
c, d 컴포넌트는 data를 사용하지 않지만 props를 받아야하고 전달자 역할을 해야한다.
따라서 간단한 애플리케이션이면 모르겠지만, 컴포넌트의 깊이가 깊은 경우, props를 이용하면 유지보수에도 어려움이 있을 수 있다.
이러한 단점을 보완할 수 있는 기능이 context API 이다.
위 그림의 의미는 A 컴포넌트에 context를 연결하면 A 컴포넌트의 하위 컴포넌트들은 context에 보관 되어있는 값(변수, state, 함수 등)에 직접적으로 접근해서 사용할 수 있다는 것이다.
최상위 컴포넌트인 app.js
가 있으며, 하위 컴포넌트로는 About.js
, PersonName.js
가 있고, PersonName.js
의 하위 컴포넌트로는 Job.js
가 있다.
import About from './components/About';
import PersonName from './components/PersonName';
function App() {
return (
<>
<PersonProvider>
<About></About>
<PersonName></PersonName>
</PersonProvider>
</>
);
}
export default App;
context
폴더를 만들고 그 안에 PersonProvider.js
파일을 생성한다.
import React, { useState, createContext } from 'react';
export const personContext = createContext();
const PersonProvider = ({ children }) => {
const [personNames, setPersonNames] = useState('홍길동');
const [about, setAbout] = useState('나는 의적이다.');
const [job, setJob] = useState('의적');
let temp = 'temp'
const printalert = (names) => {
alert(names);
}
const value = {
state: {personNames, about, job, temp},
actions : {setPersonNames, setAbout, setJob, printalert}
}
return (<personContext.Provider value={value}>{children}
</personContext.Provider>)
}
export default PersonProvider;
PersonProvider 컴포넌트에 state, setState함수, 일반 변수, 일반 함수 등을 선언했다. 그리고 이것들을 value 객체에 state, actions 객체에 담았다.
PersonProvider 바깥에 personContext
를 createContext()
를 이용하여 context를 반환할 수 있도록 하였고, PersonProvider 컴포넌트에서는 personContext.Provider
를 리턴한다.
이렇게 context를 만들 수 있다.
import './App.css';
import About from './components/About';
import PersonName from './components/PersonName';
import PersonProvider from './contexts/PersonProvider';
function App() {
return (
<>
<PersonProvider>
<About></About>
<PersonName></PersonName>
</PersonProvider>
</>
);
}
export default App;
context에 직접 접근하고자 하는 컴포넌트들을 PersonProvider의 children으로 두었다.
import React, { useContext } from 'react';
import Job from '../components/Job';
import { personContext } from '../contexts/PersonProvider';
const About = () => {
const context = useContext(personContext);
const change = () => {
context.actions.setAbout("context change");
}
return(
<div className='About'>
{context.state.about}
<button onClick={change}>change</button>
<Job></Job>
</div>
)
}
export default About;
import React, { useContext } from 'react';
import { personContext } from '../contexts/PersonProvider';
const PersonName = () => {
const context = useContext(personContext);
const change = ()=> {
context.actions.setPersonNames('change the person Name');
}
return(
<div className='PersonName' onClick={change}
style={{cursor:'pointer'}}>
{context.state.personNames}
</div>
)
}
export default PersonName;
import React, { useContext } from 'react';
import { personContext } from '../contexts/PersonProvider';
const Job = () => {
const context = useContext(personContext);
const change = () => {
context.actions.setJob('changed the job');
context.actions.printalert(context.state.job);
}
return(
<div className='Job'>
{context.state.job}
{context.state.temp}
<button onClick = {change}>chage</button>
</div>
)
}
export default Job;
위에서 언급하였듯이, 임의의 컴포넌트와 context를 연결하면 연결된 컴포넌트의 하위 컴포넌트들이 context에 직접 접근하여 사용할 수 있다.
따라서 전역적으로 context를 사용하려면 index.js의 root 컴포넌트를 Provider로 감싸야한다.
//root index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { PersonProvider } from './store.js';
ReactDOM.render(
<PersonProvider>
<App/>
</PersonProvider>,
document.getElementById("root")
);