React Native

Context API란? 소개 및 사용법

Beekei 2022. 2. 13. 20:30
반응형

Context API란?

Context API는 리액트에 내장된 기능으로 Props를 사용하지 않아도 특정 값이 필요한 컴포넌트끼리 쉽게 값을 공유할 수 있게 해 준다.

주로 프로젝트에서 전역 상태를 관리할 때 많이 사용한다. 

새로운 Context를 만들 때는 createContext 함수를 사용한다.

import { createContext } from "react";

const LogContext = createContext('Hello');

export default LogContext;

이렇게 Context를 만들면 LogContext.Provider라는 컴포넌트와 LogContext.Consumer라는 컴포넌트가 만들어진다.

Provider는 Context 안에 있는 값을 사용할 컴포넌트들을 감싸주는 용도로 사용한다.

import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import RootStack from './screens/RootStack';
import LogContext from './contexts/LogContext';


function App() {
  return (
    <NavigationContainer>
      <LogContext.Provider value="Hello World">
        <FeedsStack />
      </LogContext.Provider>
    </NavigationContainer>
  );
}

export default App;

Provider에는 value라는 Props를 설정할 수 있다. 이 값이 바로 Context를 통해 여러 컴포넌트에서 공유할 수 있는 값이다.

이렇게 Provider 컴포넌트를 사용하면 이 컴포넌트 내부에 선언된 모든 컴포넌트에서 Context안의 값을 사용할 수 있다.

 

Context 안의 값을 사용할 때는 Consumer 컴포넌트를 사용한다.

import React from "react";
import { StyleSheet, View, Text } from "react-native";
import LogContext from "../contexts/LogContext";

function FeedsStack() {
  return (
    <View>
      <LogContext.Consumer>
        {(value) => <Text>{value}</Text>}
      </LogContext.Consumer>
    </View>
  )
}

export default FeedsStack;

Context API 예제 결과

위 코드 예제에서 컴포넌트 태그 사이에 함수를 넣어줬는데, 이는 Render Props라는 패턴이다. 이 패턴을 이해하려면 우선 리액트 컴포넌트의 children이라는 Props를 이해해야 한다.

 

children Props

children Props는 우리가 Text 컴포넌트에서 사용하는 것처럼 컴포넌트 태그 사이에 넣어준 값이다.

function Box({children}) {
  return <View style={styles.box}>{children}</View>
}

function FeedsScreen() {
  return (
    <View style={styles.block}>
      <Box><Text>1</Text></Box>
      <Box><Text>2</Text></Box>
      <Box><Text>3</Text></Box>
    </View>
  )
}

이렇게 Box 컴포넌트 태그 사이 넣은 JSX를 children이라는 Props로 받아와서 사용할 수 있다.

 

Render Props는 이 children의 타입을 함수 형태로 받아오는 패턴이다.

일반적으로 컴포넌트에 필요한 값을 Props로 넣지만 Render Props를 사용하면 반대로 우리가 사용할 컴포넌트에서 특정 값을 밖으로 빼내 와 사용할 수가 있다.

function Box({children}) {
  return <View style={styles.box}>{children('Hello World')}</View>
}

function FeedsScreen() {
  return (
    <View style={styles.block}>
      <Box>{(value) => <Text>{value}</Text>}</Box>
    </View>
  )
}

 

 

FeedsScreen 컴포넌트 안에서 children을 함수로 넣어주고 Box 컴포넌트 안에서 실행하면 Box에서 FeedsScreen 컴포넌트로 값을 보내줄 수 있다.

Render Props는 리액트에 Hooks가 없던 시절 유용했는데, 요즘은 사용할 일이 그렇게 많지 않다.

밑에 useContext Hook 함수를 사용하면 더욱 간결하게 Context를 사용할 수 있다.

 

useContext Hook 함수

리액트에 useContext라는 Hook을 사용하면 Context의 값을 훨씬 간결하게 사용할 수 있기 때문에 Context의 Consumer라는 것도 꼭 사용할 필요가 없다. 

import React, { useContext } from "react";
import { StyleSheet, View, Text } from "react-native";
import LogContext from "../contexts/LogContext";

function FeedsScreen() {
  const value = useContext(LogContext);
  return (
    <View>
      <Text>{value}</Text>
    </View>
  )
}

export default FeedsScreen;

useContext를 사용하면 Render Props에 비하면 훨씬 깔끔하게 사용할 수 있다.

 

Context에서 유동적인 값 다루기

App 컴포넌트에서 useState를 사용해 관리하는 상태를 Provider로 넣어주면 값이 바뀔 때 useContext를 사용하는 컴포넌트 쪽에서도 리렌더링이 잘 발생할 것이다.

Provider를 사용하는 컴포넌트에서 Context의 상태를 관리하는 것보다는 Provider 전용 컴포넌트를 따로 만드는 것이 유지보수성이 더 높다. 특히 Context에서 다루는 로직이 복잡할 때는 전용 컴포넌트를 만드는 것이 좋다.

import React from "react";
import { createContext, useState } from "react";

const LogContext = createContext();

export function LogContextProvider({children}) {
  const [text, setText] = useState('');
  return (
    <LogContext.Provider value={{text, setText}}>
      {children}
    </LogContext.Provider>
  )
}

export default LogContext;

이렇게 LogContextProvider라는 컴포넌트로 따로 만들어줬다.

내부에서는 useState를 사용해 간단한 문자열 상태 값을 관리하고, Provider의 value에는 text와 setText를 넣어줬다.

import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { LogContextProvider } from './contexts/LogContext';
import RootStack from './screens/RootStack';

function App() {
  return (
    <NavigationContainer>
      <LogContextProvider>
        <RootStack />
      </LogContextProvider>
    </NavigationContainer>
  );
}

export default App;

LogContextProvider라는 컴포넌트 안에 FeedsScreen 컴포넌트를 넣어주면 RootStack 컴포넌트 안에서 useState로 선언된 text와 setText를 Context 통해서 공유할 수 있는 것이다.

 

RootStack 안에는 FeedsScreen, CalendarScreen 컴포넌트가 존재한다.

FeedsScreen에 TextInput 컴포넌트를 사용해 text값을 변경해주고, CalendarScreen에서 보이도록 해보겠다.

import React, { useContext, useState } from "react";
import { StyleSheet, View, Text, TextInput } from "react-native";
import LogContext from "../contexts/LogContext";

function FeedsScreen() {
  const {text, setText} = useContext(LogContext);
  return (
    <View style={styles.block}>
      <TextInput 
        value={text}
        onChangeText={setText}
        placeholder="텍스트를 입력하세요."
        style={styles.input} />
    </View>
  )
}

const styles = StyleSheet.create({
  block: {},
  input: {
    padding: 16,
    backgroundColor: 'white',
  }
})

export default FeedsScreen;

 

import React, { useContext } from "react";
import { StyleSheet, View, Text } from "react-native";
import LogContext from "../contexts/LogContext";

function CalendarScreen() {
  const {text} = useContext(LogContext);
  return (
    <View style={styles.block}>
      <Text style={styles.text}>text: {text}</Text>
    </View>
  )
}

const styles = StyleSheet.create({
  block: {},
  text: {
    padding: 16,
    fontSize: 24,
  },
})

export default CalendarScreen;

이처럼 useState와 ContextAPI로 다른 화면에서 같은 상태를 공유할 수 있다.

반응형