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;
위 코드 예제에서 컴포넌트 태그 사이에 함수를 넣어줬는데, 이는 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로 다른 화면에서 같은 상태를 공유할 수 있다.
'React Native' 카테고리의 다른 글
iOS에서 키보드가 작성한 내용을 가릴때 KeyboardAvoidingView 적용 (0) | 2022.02.15 |
---|---|
useRef로 컴포넌트 래퍼런스 선택하기 (0) | 2022.02.15 |
머티리얼 하단 탭 내비게이터(Material Bottom Tab Navigator) 사용하기 (0) | 2022.02.13 |
머티리얼 상단 탭 내비게이터(Material Top Tab Navigator) 사용하기 (0) | 2022.02.13 |
하단 탭 내비게이터(Bottom Tab Navigator) 사용하기 (0) | 2022.02.13 |