👉🏻 전일에 이어 로그인 여부에 따라 화면을 다르게 보여주는 부분에 있어, props drilling을 막고 데이터를 전달하는 방법으로 구현하기 위해 수정이 필요해보였다.
그 과정에서Context
에 대해 학습이 필요했고,Context
를 사용하여 수정한 내용을 정리해보고자 한다.
👉🏻 Context는 모든 수준에서 수동으로 전달하지 않고도 구성 요소 트리를 통해 데이터를 전달하는 방법을 제공한다.
일반적인 React 애플리케이션에서 데이터는 props를 통해 하향식으로 전달되지만, 애플리케이션 내의 많은 구성 요소에 필요한 특정 유형의 props에 대해 번거로운 문제가 있다.
그래서 prop을 명시적으로 전달하지 않고도 구성 요소 간에 값을 공유하는 방법을 제공한다는Context
를 사용하고자 했다.
👉🏻
해결하고자 했던 문제
: 유저의 로그인 여부에 따라 다른 화면으로 이동시키는데, 전일 작성했던 코드에서 생긴 props drilling 문제.
👉🏻해결에 사용한 방법
:UserContext
(UserContext.Provider
,UserContext.Consumer
)
👉🏻사고 과정
:1. UserContext.Provider가 제공하는 데이터를 사용 2. UserContext.Provider는 자식으로 있는 모든 컴포넌트에 value에 작성된 데이터를 전달 3. 데이터를 받는 곳, UserContext의 Consumer를 사용해서 render props 패턴으로 받기 4. UserContext.Consumer에서 파라미터로 전달된 setUser를 받기 5. setUser를 사용하는 onSubmit 함수에 전달하기 위해 6. onSubmit 함수를 호출하는 곳애서 setUser를 파라미터로 전달 7. onSubmit 함수에서는 전달된 setUser를 사용해서 8. signIn 함수가 성공했을때 전달된 데이터로 9. setUser(유저 상태 변수)를 수정 10. useState(유저 상태 변수)가 변경 11. AuthStack 컴포넌트 대신에 MainStack 컴포넌트를 사용해서 ListScreen 화면이 나오는 것 12. 문제 해결!
👉🏻 전일 작성했던 코드를 수정해보자!
//App.js
import { NavigationContainer } from '@react-navigation/native';
import { StatusBar } from 'expo-status-bar';
import { useState } from 'react';
import UserContext from './contexts/UserContent';
import AuthStack from './navigations/AuthStack';
import MainStack from './navigations/MainStack';
const App = () => {
//10. 유저 상태 변수가 변경되니
const [ user, setUser ] = useState(null);
return (
//1. UserContext.Provider가 제공하는 데이터를 사용
//2. UserContext.Provider는 자식으로 있는 모든 컴포넌트에 value에 작성된 데이터를 전달
//11. AuthStack 컴포넌트 대신에 MainStack 컴포넌트를 사용해서 ListScreen 화면이 나오는 것
<UserContext.Provider value={{setUser}}>
<NavigationContainer>
<StatusBar style="dark" />
{user ? <MainStack /> : <AuthStack />}
</NavigationContainer>
</UserContext.Provider>
);
};
export default App;
//SignInScreen.js
import { useEffect, useRef, useState } from 'react';
import { Alert, Image, Keyboard, StyleSheet, View } from 'react-native';
import { signIn } from '../api/auth';
import Button from '../components/Button';
import Input, {
IconNames,
KeyboardTypes,
ReturnKeyTypes,
} from '../components/Input';
import SafeInputView from '../components/SafeInputView';
import PropTypes from 'prop-types';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import UserContext from '../contexts/UserContent';
const SignInScreen = ({ navigation }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
//useRef은 값이 변해도 리렌더링 되지 않는다
//valueRef.current에 값이 들어간다
const passwordRef = useRef(null);
const [disabled, setDisabled] = useState(true);
const [isLoading, setIsLoading] = useState(false);
const insets = useSafeAreaInsets();
useEffect(() => {
setDisabled(!email || !password);
}, [email, password]);
//5. setUser를 사용하는 onSubmit 함수에 전달하기 위해
//7. onSubmit 함수에서는 전달된 setUser를 사용해서
const onSubmit = async (setUser) => {
if (!disabled && !isLoading) {
Keyboard.dismiss();
setIsLoading(true);
try {
//8. signIn 함수가 성공했을때 전달된 데이터로
const data = await signIn(email, password);
setIsLoading(false);
//9. 유저 상태 변수를 수정했다
setUser(data);
//화면 이동
navigation.navigate('List');
} catch (e) {
Alert.alert('SignIn Failed', e, [
{
text: 'OK',
onPress: () => setIsLoading(false),
},
]);
}
}
};
return (
//3. 데이터를 받는 곳, UserContext의 Consumer를 사용해서 render props 패턴으로 받으면 된다
//4. 파라미터로 전달된 setUser를 받아와서
<UserContext.Consumer>
{({ setUser}) => {
return (
<SafeInputView>
<View
style={[
styles.container,
{ paddingTop: insets.top, paddingBottom: insets.bottom },
]}
>
<Image
source={require('../../assets/main.png')}
style={styles.image}
resizeMode={'cover'}
/>
<Input
value={email}
onChangeText={(text) => setEmail(text.trim())}
title={'email'}
placeholder={'your@email.com'}
keyboardType={KeyboardTypes.EMAIL}
returnKeyType={ReturnKeyTypes.NEXT}
iconName={IconNames.EMAIL}
onSubmitEditing={() => passwordRef.current.focus()}
/>
<Input
ref={passwordRef}
value={password}
onChangeText={(text) => setPassword(text.trim())}
title={'password'}
secureTextEntry
iconName={IconNames.PASSWORD}
onSubmitEditing={() => onSubmit(setUser)}
/>
<View style={styles.buttonContainer}>
<Button
title={'LOGIN'}
// 6. onSubmit 함수를 호출하는 곳애서 setUser를 파라미터로 전달
onPress={() => onSubmit(setUser)}
disabled={disabled}
isLoading={isLoading}
/>
</View>
</View>
</SafeInputView>
);
}}
</UserContext.Consumer>
);
};
SignInScreen.propTypes = {
navigation: PropTypes.object,
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
image: {
width: 200,
height: 200,
},
buttonContainer: {
width: '100%',
paddingHorizontal: 20,
marginTop: 20,
},
});
export default SignInScreen;