Search

useContext

useContext() 훅은 리액트에서 상태를 전역적(globally)으로 사용할 수 있도록 해줍니다. useState()로 생성한 상태는 기본적으로 지역적(locally)이지만, useContext()와 함께 사용하면 상태를 전역적으로 사용할 수 있어집니다.
사용 예를 들기 위해, 앱 화면에서 밝은 색상을 사용할 라이트 모드와 어두운 색상을 사용할 다크 모드를 useState()를 통해 theme 상태로 만든 뒤 모든 컴포넌트에 적용시켜 사용한다고 가정해 봅시다. 이때 색상을 처리하지 않는 컴포넌트들은 theme 상태를 사용할 필요가 없을겁니다. 하지만 자식 컴포넌트 중 어딘가 theme 상태가 필요한 경우가 있을 것이므로 불필요 하더라도 매번 theme 상태를 props로 받아 자식 컴포넌트에게 다시 props로 지정해줘야 하는 비효율적인 구조가 되게 됩니다.
 useState만 사용해서 앱 컴포넌트에 전체에 theme 상태를 공유하려고 하면 모든 컴포넌트마다 props로 theme 상태를 지정해 내려줘야하는 큰 불편함이 생기게 됩니다.
이때 theme 상태를 매번 props로 지정해서 내려주지 않고 한번에 전역적으로 사용할 수 있게 하려면 useContext()를 활용하면 됩니다. Context를 생성하고 theme 상태를 지정해서 루트에 위치시켜 주면, 값이 필요한 컴포넌트는 어디서든 useContext()를 호출해서 theme 상태를 사용할 수 있습니다.
 useContext와 함께 useState를 사용하면 상태를 전역적으로 관리하므로 필요한 곳에서만 useContext 함수로 호출해서 사용합니다.
그럼 본격적으로 useContext() 사용 방법에 대해 알아봅시다. 위에서 언급한 theme 상태의 전역적 사용을 계속 예로 들어 설명할 것입니다.

Context 생성

useContext()를 사용하기 위해서는 먼저 createContext() 함수로 고유한 Context를 먼저 생성해야 합니다. 생성할 Context는 명시적으로 Context라는 것을 나타내기 위해 특정 명칭 뒤에 Context를 붙여주는 것이 일반적입니다. Context는 컴포넌트처럼 사용되므로 파스칼 케이스(PascalCase)로 작성해 줍시다.
import { createContext } from 'react'; const ThemeContext = createContext();
JavaScript
복사

Context Provider

생성한 Context를 하위 컴포넌트에서 사용하게 하려면 사용할 모든 하위 컴포넌트의 루트에 Context Provider로 감싸주어야 합니다. 사용 방법은 생성한 Context 객체의 Provider 컴포넌트로 감싸주면 됩니다. <Context.Provider></Context.Provider> 처럼 JSX 구문 내에서 컴포넌트로 사용해서 감싸줍니다.
그러고 나서 useState()로 생성한 상태를 Context Providervalue로 지정해주면 모든 하위 컴포넌트에서 같은 상태를 사용할 수 있게 됩니다.
import { createContext, useState } from 'react'; import ReactDOM from 'react-dom/client'; const ThemeContext = createContext(); // Context 생성 const App = () => { const [theme, setTheme] = useState('dark'); // 상태 생성 return ( // Context Provider 컴포넌트로 하위 컴포넌트들을 감싸준 뒤, // 하위 컴포넌트에게 공유할 상태를 value 값으로 지정 <ThemeContext.Provider value={theme}> {/* <Content /> 감싸줄 하위 컴포넌트 */} </ThemeContext.Provider> ); }; const rootNode = document.getElementById('root'); ReactDOM.createRoot(rootNode).render(<App />);
JavaScript
복사

useContext

Context Provider 범위 내에 속한 컴포넌트라면 useContext()를 호출해서 Context Provider에 지정한 value 값을 불러와 사용할 수 있습니다. 이때 사용할 Context 객체를 useContext()의 인자로 포함시켜야 합니다.
import { createContext, useContext, useState } from 'react'; import ReactDOM from 'react-dom/client'; const ThemeContext = createContext(); const App = () => { const [theme, setTheme] = useState('dark'); return ( <ThemeContext.Provider value={theme}> <Content /> </ThemeContext.Provider> ); }; const Content = () => { // useContext 함수로 Context의 value 값을 가져옴 const theme = useContext(ThemeContext); return ( <div style={{ backgroundColor: theme === 'dark' ? 'black' : 'white' }}> <h1 style={{ color: theme === 'dark' ? 'white' : 'black' }}> {`현재 화면은 ${theme}모드 입니다.`} </h1> </div> ); } const rootNode = document.getElementById('root'); ReactDOM.createRoot(rootNode).render(<App />);
JavaScript
복사
상태를 사용할 하위 컴포넌트에서만 useContext()로 전역 상태를 불러와 사용하면 됩니다.
상태를 사용하지 않는 컴포넌트가 중간에 있더라도 문제 없이 자식 컴포넌트에서 useContext()로 상태를 불러와 사용할 수 있습니다.
import { createContext, useContext, useState } from 'react'; import ReactDOM from 'react-dom/client'; const ThemeContext = createContext(); const App = () => { const [theme, setTheme] = useState('dark'); return ( <ThemeContext.Provider value={theme}> {/* 상태를 사용하지 않는 Main 컴포넌트가 App과 Content 사이에 존재 */} <Main /> </ThemeContext.Provider> ); }; const Main = () => { return <Content />; }; const Content = () => { const theme = useContext(ThemeContext); return ( <div style={{ backgroundColor: theme === 'dark' ? 'black' : 'white' }}> <h1 style={{ color: theme === 'dark' ? 'white' : 'black' }}> {`현재 화면은 ${theme}모드 입니다.`} </h1> </div> ); }; const rootNode = document.getElementById('root'); ReactDOM.createRoot(rootNode).render(<App />);
JavaScript
복사
useContext를 사용하는 Content 컴포넌트 상위에 Main 컴포넌트가 존재하지만 문제 없이 App 컴포넌트에서 선언된 상태를 Content 컴포넌트에서 사용 가능합니다.
상태를 생성할때, useState()구조분해할당으로 반환 받지 않고 배열 그대로 반환 받은 다음 Context Provider의 value 값으로 지정하여 하위 컴포넌트에서 useContext()를 마치 useState()처럼 사용하도록 할 수도 있습니다. 이런 경우 value 값이 배열이므로 useContext() 반환 값에 구조분해할당 문법을 적용해서 사용합니다.
import { createContext, useContext, useState } from 'react'; import ReactDOM from 'react-dom/client'; const ThemeContext = createContext(); const App = () => { // 구조분해할당 적용하지 않고 배열로 생성한 뒤 value 값으로 지정 const themeState = useState('dark'); return ( <ThemeContext.Provider value={themeState}> <Main /> </ThemeContext.Provider> ); }; const Main = () => { return ( <> <Content /> <Button /> </> ); }; const Content = () => { // 하위컴포넌트에서 useContext 함수 반환에 구조분해할당 적용해서 사용 const [theme] = useContext(ThemeContext); return ( <div style={{ backgroundColor: theme === 'dark' ? 'black' : 'white' }}> <h1 style={{ color: theme === 'dark' ? 'white' : 'black' }}> {`현재 화면은 ${theme}모드 입니다.`} </h1> </div> ); }; const Button = () => { // 하위컴포넌트에서 useContext 함수 반환에 구조분해할당 적용해서 사용 const [theme, setTheme] = useContext(ThemeContext); // 클릭하면 테마 토글하는 함수 const onClick = () => { setTheme((currentTheme) => currentTheme === 'dark' ? 'light' : 'dark'); }; return ( <button onClick={onClick}> {`${theme === 'dark' ? 'light' : 'dark'}테마로 변경`} </button> ); }; const rootNode = document.getElementById('root'); ReactDOM.createRoot(rootNode).render(<App />);
JavaScript
복사

커스텀 Hook으로 만들어 사용

useContext()를 반환하는 함수를 별도로 만들면 일종의 커스텀 Hook이 됩니다. 이렇게 만든 커스텀 Hook만 하위 컴포넌트에서 호출해서 사용하는 방법도 있습니다.
import { createContext, useContext, useState } from 'react'; import ReactDOM from 'react-dom/client'; const ThemeContext = createContext(); // useContext를 반환하는 커스텀 Hook const useTheme = () => { return useContext(ThemeContext); }; const App = () => { const themeState = useState('dark'); return ( <ThemeContext.Provider value={themeState}> <Main /> </ThemeContext.Provider> ); }; const Main = () => { return ( <> <Content /> <Switch /> </> ); }; const Content = () => { // useContext대신 useTheme 사용 const [theme] = useTheme(); return ( <div style={{ backgroundColor: theme === 'dark' ? 'black' : 'white' }}> <h1 style={{ color: theme === 'dark' ? 'white' : 'black' }}> {`현재 화면은 ${theme}모드 입니다.`} </h1> </div> ); }; const Switch = () => { // useContext대신 useTheme 사용 const [theme, setTheme] = useTheme(); const onClick = () => { setTheme((currentTheme) => currentTheme === 'dark' ? 'light' : 'dark'); }; return ( <button onClick={onClick}> {`${theme === 'dark' ? 'light' : 'dark'}테마로 변경`} </button> ); }; const rootNode = document.getElementById('root'); ReactDOM.createRoot(rootNode).render(<App />);
JavaScript
복사
여기에 Context Provider를 반환하는 함수형 컴포넌트를 독립적으로 만들어서 사용하면 조금 더 깔끔해집니다. 이렇게 하면 Context 관련 설정 코드를 일반 컴포넌트에서 완전히 분리시켜서 사용할 수 있어집니다. Context Provider는 자식 컴포넌트를 감싸는 형태를 갖추어야 하기 때문에 자식 요소에 대응하는 props.children을 사용합니다. children을 Context Provider가 감싸는 <Context.Provider>{children}</Context.Provider>와 같은 형태로 반환해 줍니다.
import { createContext, useContext, useState } from 'react'; import ReactDOM from 'react-dom/client'; const ThemeContext = createContext(); // value 값이 고정된 ThemeProvider 컴포넌트 // 하위 요소를 포함해야 하기 때문에 하위 요소에 대응하는 children 프로퍼티를 // ThemeContext.Provider로 감싸줌 const ThemeProvider = ({ children }) => { const themeState = useState('dark'); return ( <ThemeContext.Provider value={themeState}> {children} </ThemeContext.Provider> ); }; const useTheme = () => { return useContext(ThemeContext); }; const App = () => { // 기존 ThemeContext.Provider 대신 ThemeProvider 컴포넌트 사용 return ( <ThemeProvider> <Main /> </ThemeProvider> ); }; const Main = () => { return ( <> <Content /> <Switch /> </> ); }; const Content = () => { const [theme] = useTheme(); return ( <div style={{ backgroundColor: theme === 'dark' ? 'black' : 'white' }}> <h1 style={{ color: theme === 'dark' ? 'white' : 'black' }}> {`현재 화면은 ${theme}모드 입니다.`} </h1> </div> ); }; const Switch = () => { const [theme, setTheme] = useTheme(); const onClick = () => { setTheme((currentTheme) => currentTheme === 'dark' ? 'light' : 'dark'); }; return ( <button onClick={onClick}> {`${theme === 'dark' ? 'light' : 'dark'}테마로 변경`} </button> ); }; const rootNode = document.getElementById('root'); ReactDOM.createRoot(rootNode).render(<App />);
JavaScript
복사
이때 컴포넌트들을 파일 단위로 분리해서 사용한다면 useContext를 활용해서 만든 커스텀 Hook을 export한 뒤 다른 파일에서 import하여 사용하면 됩니다.
./useTheme.js
import { createContext, useContext, useState } from 'react'; const ThemeContext = createContext(); export const ThemeProvider = ({ children }) => { const themeState = useState('dark'); return ( <ThemeContext.Provider value={themeState}> {children} </ThemeContext.Provider> ); }; export const useTheme = () => { return useContext(ThemeContext); }; export default useTheme;
JavaScript
복사
Context 관련 코드만 모아 커스텀 Hook 파일로 만들어 줍니다. 다른 파일에서 import해서 사용할 수 있도록 함수와 컴포넌트에 export를 추가해 줍니다.
./App.js
import ReactDOM from 'react-dom/client'; import { ThemeProvider } from './useTheme'; // Provider 컴포넌트를 import해서 사용 import Main from './Main' const App = () => { return ( <ThemeProvider> <Main /> </ThemeProvider> ); }; export default App; const rootNode = document.getElementById('root'); ReactDOM.createRoot(rootNode).render(<App />);
JavaScript
복사
./Main.js
import Content from './Content'; import Switch from './Switch'; const Main = () => { return ( <> <Content /> <Switch /> </> ); }; export default Main;
JavaScript
복사
./Content.js
import { useTheme } from './useTheme'; // 커스텀 Hook을 import해서 사용 const Content = () => { const [theme] = useTheme(); return ( <div style={{ backgroundColor: theme === 'dark' ? 'black' : 'white' }}> <h1 style={{ color: theme === 'dark' ? 'white' : 'black' }}> {`현재 화면은 ${theme}모드 입니다.`} </h1> </div> ); }; export default Content;
JavaScript
복사
./Switch.js
import { useTheme } from './useTheme'; // 커스텀 Hook을 import해서 사용 const Switch = () => { const [theme, setTheme] = useTheme(); const onClick = () => { setTheme((currentTheme) => currentTheme === 'dark' ? 'light' : 'dark'); }; return ( <button onClick={onClick}> {`${theme === 'dark' ? 'light' : 'dark'}테마로 변경`} </button> ); }; export default Switch;
JavaScript
복사

 useEffect

 useRef

React Hooks 목록