여행 사진 공유앱을 시작한다.
시작할 때 깔아야 하는 애들..
"dependencies": {
"@react-navigation/native": "^6.1.6",
"expo": "~48.0.15",
"expo-status-bar": "~1.4.4",
"prop-types": "^15.8.1",
"react": "18.2.0",
"react-native": "0.71.7",
"react-native-safe-area-context": "4.5.0",
"react-native-screens": "~3.20.0"
},
뭐 많이 만들었다.
로그인/회원가입 페이지를 만들었고 Input, Button 등은 이전 ToDo와 동일하게 가져간다 보면 될 듯. 그래도 Memo 용으로 적당한 것들을 기록해보면
1. 키보드 높이로 인해 가려지는 부분은 Scroll을 사용한다.
<ScrollView
style={[styles.form, { paddingBottom: bottom ? bottom + 10 : 40 }]}
contentContainerStyle={{ alignItems: 'center' }}
bounces={false}
keyboardShouldPersistTaps={'always'}
>
<Input
inputType={InputTypes.EMAIL}
value={email}
onChangeText={text => setEmail(text.trim())}
onSubmitEditing={() => passwordRef.current.focus()}
styles={{ container: { marginBottom: 20 } }}
returnKeyType={ReturnkeyTypes.NEXT}
></Input>
<Input
ref={passwordRef}
inputType={InputTypes.PASSWORD}
value={password}
onChangeText={text => setPassword(text.trim())}
onSubmitEditing={onSubmit}
styles={{ container: { marginBottom: 20 } }}
returnKeyType={ReturnkeyTypes.DONE}
></Input>
<Button
title="SIGNIN"
onPress={onSubmit}
disabled={disabled}
isLoading={isLoading}
styles={{
container: {
marginTop: 20,
},
}}
></Button>
<HR text={'OR'} styles={{ container: { marginVertical: 20 } }} />
<TextButton
title={'SIGNUP'}
onPress={() => {
navigation.navigate(AuthRoute.SIGN_UP);
}}
/>
</ScrollView>
scrollView로 감쌀 때, Style 중 AlignMent 나 Justify 와 연관된 내용은 contentContainerStyle 속성안에 넣어야 한다.
bounces={false} : 끌림 방지 keyboardShouldPersistTaps={‘always’} : 키보드 나올 때 버튼 누르면 이벤트 발생하도록
2. useFocusEffect
useFocusEffect(
useCallback(() => {
return () => {
setEmail('');
setPassword('');
setisLoading(false);
setDisabled(true);
};
}, []),
);
useFocusEffect는 해당 화면이 나타날 때 발생하며 정리함수를 사용하면, 해당 화면에서 벗어날 때 값을 초기화 해줄 수 있다.
이 때, useCallback 안에 사용하여 함수를 캐시에 저장해두고 사용함으로써 중복으로 호출되는 현상을 방지한다.
useReducer
상태 관리하는 hook으로 usestate 말고도 useReducer라는게 있음.
const [state, dispatch] = useReducer(reducer, init);
state: 상태 변수
dispatch : reducer로 action 을 전달하는 함수
action : 현재 상태를 어떻게 변경해야하는지에 대한 행동 지침
reducer : state와 action을 받아서 변경된 상태를 변환하는 함수
사용예시는 다음과 같다.
const init = 0;
const CountType = {
INCREMENT: 'INCREMENT',
DECREMENT: 'DECREMENT',
};
const reducer = (state, action) => {
switch (action) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
};
...
const [resultVal, dispatch] = useReducer(reducer, init);
return (
<View style={styles.container}>
<Text style={styles.title}>{resultVal}</Text>
<Button title={'+'} onPress={() => dispatch(CountType.INCREMENT)} />
<Button title={'-'} onPress={() => dispatch(CountType.DECREMENT)} />
</View>
);
위와 같이 사용하면 dispatch 를 통해 action 값을 전달해주고 reducer 안에서 action을 받아서 그에 따라 동작하게 만들어주면 됨.
더 직관적으로 사용할 수 있게 된다. 값을 전달할수도 있어서, 전달된 값을 더하거나 뺄 수도 있다.
reducer는 반드시 순수함수여야한다.
순수함수란 입력데이터를 의존하지 않고, 외부 데이터를 변형하지 않는다. 그냥 받아서 사용하기만 하면 됨. state나 action을 변형시키면 안됨 그냥 딱 result 만 줘야 함.
순수함수가 아니면?? 동작하지 않는다.
원리에 대해서도 확인해주는데 객체를 변경할 때, 주소값을 변경해주기 때문에 어차피 변경되는 것이 없다고 판단한다..
화면전환에 흰화면 뜸
화면 구동 시… 파일을 읽어오는데 구동되는데 시간이 걸리므로 캐싱함.
assets를 cache에 넣으려면?
SplashScreen 이라는걸 사용하면 됨.
App.js 에
useEffect(() => {
(async () => {
try {
SplashScreen.preventAutoHideAsync();
// Image 캐싱
} catch (error) {
console.log(error);
}
})();
}, []);
위코드를 두면 preventAutoHideAsync에 의해서 처음에 빌드가 다 되어도 SplashScreen을 계속 띄운다.
최종 코드
useEffect(() => {
(async () => {
try {
await SplashScreen.preventAutoHideAsync();
await Asset.fromModule(
require('../assets/cover.png'),
).downloadAsync();
// Image 캐싱
} catch (error) {
console.log(error);
} finally {
setIsReady(true);
}
})();
}, []);