Context API

Context API란 React에서 제공하는 내장 API로서 컴포넌트들에게 동일한 Context(맥락)을 전달하는데 사용할 수 있다.

일반적으로 리액트에서 데이터를 전달하는 기본 원칙은 단방향성이며, 부모 컴포넌트에서 자식 컴포넌트 방향으로만 데이터를 전달할 수 있다는 의미이다.

단방향성은 애플리케이션의 안전성을 높이고 흐름을 단순화하는데 유용하지만 때로는 너무 많은 단계를 거쳐서 자식 컴포넌트에 데이터를 전달해야 한다는(props drilling)문제점을 야기하기도 한다.

컴포넌트의 구조를 잘 설계하고 합성을 적극적으로 활용해 데이터를 계속해서 넘겨줘야 하는 상황을 안만드는 것이 최선이지만, 해당 방법으로 해결이 안될 때는 Context API를 사용하여 문제를 해결할 수 있다.

Context API는 상태관리 도구 인가?

Context API는 Redux나 Recoil 같은 상태관리 라이브러리와 같은 역할을 하는가? 에 대해서는 조금 다른 이야기다. 왜냐하면 Context API는 여러 컴포넌트에 동일한 값을 접근, 공유할 수 있도록 해주는 역할이다. 값을 저장하거나 업데이트하는 기능은 없다. 단지 Provider를 통해 의존성 주입을 통해 필요한 하위 컴포넌트에서 값을 내려받는 것일 뿐이다.

반대로 Redux 같은 상태관리 라이브러리는 값을 저장, 변경 등 상태에 대해 여러가지 처리가 가능하므로 상태관리 라이브러리라 할 수 있다는 것이다.

굳이 따지자면, Context API + useState / useReducer를 결합한다면 유사한 상태관리 도구라고 할 수 도 있겠다.

https://blog.isquaredsoftware.com/2021/01/context-redux-differences/

Context API 사용법

createContext

Context API를 사용하기 위해서는 먼저 공유할 Context를 만들어줘야 한다. Context는 createContext 라는 함수를 통해서 사용할 수 있다.

const UserContext = createContext(null);
  • createContext 함수를 호출하면 Context 객체가 리턴된다.
  • 함수를 호출할 때는 defaultValue를 인자로 전달할 수 있다.

이때 defaultValue는 Context Value의 초기값이 아닌, 다른 컴포넌트에서 Context에 접근하려고 하지만 Provider로 감싸져 있지 않은 상황에서 사용될 값을 의미합니다.

Provider

만들어진 Context를 통해서 특정한 값을 전달하기 위해서는 Provider 컴포넌트를 이용해야 한다.

Context 객체에는 Provider라는 프로퍼티가 있으며 이는 리액트 컴포넌트입니다.

Provider 컴포넌트는 value라는 props을 가지고 있으며,value에 할당된 값을 Provider 컴포넌트 하위에 있는 어떤 컴포넌트든 접근할 수 있게 해주는 기능을 가지고 있다.

const UserContext = createContext(null);

const user = {name: "kim"};

<UserContext.Provider value={user}>
	<Child />
</UserContext.Provider>

useContext

클래스 컴포넌트에서 Context를 통해 공유된 값에 접근하려면, Consumer라는 다소 복잡한 방식을 통해서 접근해야 한다. 하지만 함수 컴포넌트에서는 useContext라는 내장 Hook을 이용해 Context Value에 접근할 수 있다.

const UserContext = createContext(null);

const user = {name: "kim"};

<UserContext.Provider value={user}>
	<Child />
</UserContext.Provider>

function Child() {
	const user = useContext(UserContext);
	
	return <h1>{user.name}</h1>
}

Context API를 활용한 다크모드 설정

App.js

import { ThemeProvider, useTheme, useThemeChange } from "./ThemeContext";
import "./styles.css";

export default function App() {
  return (
    <ThemeProvider>
      <Title />
      <ToggleThemeButton />
    </ThemeProvider>
  );
}

const Title = () => {
  const theme = useTheme();

  const style = {
    color: theme?.isDarkMode() ? "white" : "black",
    backgroundColor: theme?.isDarkMode() ? "black" : "white"
  };

  return <h1 style={style}>Hello, World</h1>;
};

const ToggleThemeButton = () => {
  const toggleTheme = useThemeChange();

  return <button onClick={toggleTheme}>change theme</button>;
};

ThemeContext.js

import { createContext, useCallback, useContext, useState } from "react";

const ThemeContext = createContext(null);
const ThemeChangeContext = createContext(null);

export const useTheme = () => useContext(ThemeContext);
export const useThemeChange = () => useContext(ThemeChangeContext);

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState("light");

  const isDarkMode = useCallback(() => theme === "dark", [theme]);
  const isLightMode = useCallback(() => theme === "light", [theme]);

  const toggleTheme = useCallback(
    () => setTheme((prevTheme) => (prevTheme === "light" ? "dark" : "light")),
    [setTheme]
  );

  return (
    <ThemeContext.Provider value={{ isDarkMode, isLightMode }}>
      <ThemeChangeContext.Provider value={toggleTheme}>
        {children}
      </ThemeChangeContext.Provider>
    </ThemeContext.Provider>
  );
}
profile
기억보다는 기록하는 개발자

0개의 댓글

Powered by GraphCDN, the GraphQL CDN