프로젝트
오늘 한 일 1
- 설정페이지에서 닉네임 변경 및 로그아웃 기능 구현
- 닉네임 변경
- 파이어베이스에서 제공하는
updateProfile
메서드를 사용해 처음에 저장한 displayName(닉네임) 값을 Input에 입력한 값으로 변경해줌
- 하지만 새로고침을 하지 않으면 바로 화면에 렌더링 되지 않는 현상 발생
- 위와 같은 문제가 생기는 이유는 리액트의 동작 원리때문이다. 리액트는 변화된 값만을 감지해서 상태를 변화시키는데에 최적화 되어있다. 그러나 authService.currentUser 내에는 너무 많은 데이터가 담겨있고, 이를 리액트가 모두 하나하나 비교할 수는 없기 때문이다.
- 따라서 userObj에 currentUser객체를 모두 넣지않고 필요한것만 넣어서 리액트가 비교하기 편하도록 설계해야한다.
- 현재 우리가 필요한 currentUser의 속성과 메서드는 displayName, uid 와 updateProfile 메서드 뿐이다.
- 따라서 처음 useEffect로 UserObj를 변경할때부터 위 값만을 넣도록 만들어었다.
- 로그아웃
- 로그아웃은 간단하게 파이어베이스에서 제공하는
singOut
메서드를 이용해서 기능을 구현하였다.
- Alert를 사용해서 바로 로그아웃 되지 않게 UI,UX 면에 신경썼다.
import styled from '@emotion/native';
import { View, Text, Alert, useColorScheme } from 'react-native';
import { SCREEN_HEIGHT, SCREEN_WIDTH } from '../utils';
import { signOut } from 'firebase/auth';
import { authService, dbService } from '../firebase';
import { useFocusEffect, useNavigation } from '@react-navigation/native';
import { useCallback, useEffect, useState } from 'react';
import { getAuth, updateProfile } from 'firebase/auth';
import profileImg from '../assets/profileImg.png';
import { ScrollView, TouchableOpacity } from 'react-native-gesture-handler';
import { AntDesign } from '@expo/vector-icons';
import { ORANGE_COLOR, BLUE_COLOR, DARK_COLOR } from '../colors';
export default function Settings() {
const isDark = useColorScheme() === 'dark';
const { navigate, setOptions, goBack } = useNavigation();
const [textValue, setTextValue] = useState('');
const [init, setInit] = useState(false);
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [userObj, setUserObj] = useState(null);
const auth = getAuth();
const user = auth.currentUser;
const displayName = user?.displayName;
const editNickName = async () => {
await updateProfile(auth.currentUser, {
displayName: textValue,
})
.then(() => {
setTextValue('');
})
.catch((error) => {
console.log(error);
});
refreshUser();
};
useEffect(() => {
authService.onAuthStateChanged((user) => {
if (user) {
setIsLoggedIn(true);
setUserObj({
displayName: user.displayName,
uid: user.uid,
updateProfile: (args) => user.updateProfile(args),
});
} else {
setIsLoggedIn(false);
}
setInit(true);
});
}, []);
const refreshUser = () => {
const user = authService.currentUser;
setUserObj({
displayName: user.displayName,
uid: user.uid,
updateProfile: (args) => user.updateProfile(args),
});
};
const logout = () => {
Alert.alert('로그아웃', '로그아웃 하시겠습니까?', [
{
text: '취소',
style: 'cancel',
onPress: () => console.log('취소 클릭!'),
},
{
text: '로그아웃',
style: 'destructive',
onPress: () => {
signOut(authService);
navigate('NotTabs', { screen: 'Login' });
},
},
]);
};
useFocusEffect(
useCallback(() => {
setOptions({
headerLeft: () => (
<TouchableOpacity onPress={() => goBack()}>
<AntDesign
name='left'
size={24}
style={{ marginLeft: 16 }}
color={isDark ? ORANGE_COLOR : BLUE_COLOR}
/>
</TouchableOpacity>
),
});
}, [])
);
return (
<>
{!!user ? (
<SettingWrap style={{ backgroundColor: isDark ? '#1B1D21' : 'white' }}>
<View style={{ marginTop: -22 }}>
<SettingImage source={profileImg} />
</View>
<ProfileView
style={{ backgroundColor: isDark ? '#3320B3' : BLUE_COLOR }}
>
<ProfileTextWrap>
<ProfileTitle>{displayName}</ProfileTitle>
<ProfileTextInput
placeholder='닉네임을 입력해주세요 ...'
placeholderTextColor='#A8A8A8'
value={textValue}
onChangeText={setTextValue}
/>
</ProfileTextWrap>
<ProfileButton onPress={editNickName}>
<ProfileButtonText
style={{ backgroundColor: isDark ? '#3320B3' : BLUE_COLOR }}
>
수정하기
</ProfileButtonText>
</ProfileButton>
</ProfileView>
<LogoutButton
onPress={logout}
style={{ backgroundColor: isDark ? '#3320B3' : BLUE_COLOR }}
>
<LogoutButtonText>로그아웃</LogoutButtonText>
</LogoutButton>
</SettingWrap>
) : (
<VisitorView>
<Text>로그인이 필요한 서비스입니다.</Text>
</VisitorView>
)}
</>
);
}
const SettingWrap = styled.View`
flex: 1;
padding: 10% 15%;
`;
const SettingImage = styled.Image`
height: ${SCREEN_HEIGHT / 3.5 + 'px'};
width: ${SCREEN_WIDTH / 2 + 'px'};
margin-bottom: 10%;
margin-left: 15%;
`;
const ProfileView = styled.View`
justify-content: space-around;
align-items: center;
height: ${SCREEN_HEIGHT / 3.5 + 'px'};
background-color: #0c68f2;
border-radius: 20%;
`;
const ProfileTextWrap = styled.View`
align-items: center;
width: ${SCREEN_WIDTH / 2 + 'px'};
border-bottom-width: 1px;
border-bottom-color: white;
`;
const ProfileTitle = styled.Text`
font-size: 20px;
font-weight: bold;
color: white;
margin-bottom: 30px;
`;
const ProfileTextInput = styled.TextInput`
color: white;
/* &::placeholder {
color: #A8A8A8;
} */
margin-bottom: 3%;
`;
const ProfileButton = styled.TouchableOpacity`
width: 150px;
height: 30px;
border-width: 1px;
border-color: white;
border-radius: 20%;
justify-content: center;
align-items: center;
`;
const ProfileButtonText = styled.Text`
color: white;
`;
const LogoutButton = styled.TouchableOpacity`
margin-top: 10%;
border-radius: 30%;
width: ${SCREEN_WIDTH};
height: 50px;
background-color: #0c68f2;
justify-content: center;
align-items: center;
`;
const LogInButton = styled.TouchableOpacity`
margin-top: 10%;
border-radius: 30%;
width: ${SCREEN_WIDTH};
height: 50px;
background-color: #0c68f2;
justify-content: center;
align-items: center;
`;
const LogoutButtonText = styled.Text`
color: white;
`;
const VisitorView = styled.View`
height: ${SCREEN_HEIGHT / 1.5 + 'px'};
justify-content: center;
align-items: center;
`;
오늘 한 일 2
- 관심목록에 추가하기 기능 ( 미완성 )
- 관심목록에 추가하기 기능을 구현하기 위해서 일단 가져온 데이터들을 파이어베이스에 저장한다.
- 한 번에 모두 저장하면 데이터가 많아지기 때문에 메인페이지에서 디테일 페이지로 클릭할 때 데이터가 추가되도록 한다.
- 생각해보니 클릭했던 것을 또 클릭하면 계속 추가되기 때문에 추가되었던 데이터가 가져온 데이터와 일치하면 추가되지 않도록 막아주었다.
- 추가할 때 api데이터 키-값에 isLike: false 를 추가하여 하트 버튼을 누르면 true : false 가 되도록 해주었다.
- onSnapshot 메서드를 이용하고 상위컴포넌트에서 useState로 데이터를 저장해주었는데, 메인 페이지를 벗어나는 순간 코드들이 죽어 업데이트 된 데이터가 넘어오지 않는 현상이 발생하였다.