본문 바로가기
스파르타코딩클럽/내일배움캠프

[TIL] 내일배움캠프 React 과정 2023.01.05

by heereal 2023. 1. 5.

Today I Learned

  • React Native 심화 강의 수강

 


!!(느낌표 두 개) 연산자가 뭐지?

let a = 1
console.log(!a) // false
console.log(!!a) // true

let a = undefined
console.log(!!a) // false

해당 변수에 값이 할당되어있으면 true, 그렇지 않으면 false로 반환해주는 매우 유용한 연산자이다!

 

참고

https://hermeslog.tistory.com/279

https://velog.io/@hoon_dev/JavaScript-느낌표-두개Double-Exclamation-Marks-연산자-알아보기

 

 

FlatList 연구하기

<FlatList 
    refreshing={isRefreshing}
    onRefresh={onRefresh}
    ListHeaderComponent={
        <>
            <Swiper height="100%" showsPagination={false} autoplay>
                {nowPlaying.map((movie) => (
                    <Slide key={movie.id} movie={movie} />
                ) )}
            </Swiper>

            <ListTitle>Top Rated Movies</ListTitle>
            <FlatList 
                horizontal
                contentContainerStyle={{ paddingHorizontal: 10 }}
                showsHorizontalScrollIndicator={false}
                data={topRated}
                renderItem={({ item }) => <TopRatedCard movie={item} />}
                keyExtractor={(item) => item.id}
                // ItemSeparatorComponent={ <View style={{ width: 10 }} /> }
            />
            <ListTitle>Upcoming Movies</ListTitle>
        </>
    }
    data={upcoming}
    renderItem={({ item }) => <UpcomingCard movie={item} />}     
    keyExtractor={(item) => item.id}
    // ItemSeparatorComponent={<View style={{ height: 10}} />}
/>

FlatList 안에 ListHeaderComponent를 넣는 이유를 이해하고 싶어서 동기분들과 튜터님들을 좀 괴롭히고 다녔다. 일단 FlatList가 기본적으로 스크롤 기능을 내장하고 있는데 만약에 ListHeaderComponent로 넣지 않고 Top Rated Movies와 Upcoming Movies를 각각 FlatList로 구현한다면 최상단 스크롤뷰 안에 스크롤뷰가 들어가는 구조와 동일해지기 때문에 문제가 된다.

"VirtualizedLists should never be nested inside plain ScrollViews with the same orientation - use another VirtualizedList-backed container instead."
vertical 스크롤뷰 안에 horizontal이 들어가는 건 문제가 안되지만(Top Rated Movies는 문제 없음) vertical 안에 vertical이 들어가는 건(Upcoming Movies의 경우) 안 된다~ 그래서 스크롤뷰 안에 스크롤뷰가 들어가는 것을 방지하기 위해서 FlatList 안에 ListHeaderComponent를 따로 빼줬던 것이다.

 

참고 https://blog.smilecat.dev/posts/template-library-react-native

 

 

firebase로 로그인 및 회원가입 구현하기

const validInputs = () => {
    if (!email) {
        alert("email을 입력해 주세요.");
        emailRef.current.focus();
        return true;
    }

    if (!pw) {
        alert("password를 입력해 주세요.");
        pwRef.current.focus();
        return true;
    }

    const matchedEmail = email.match(emailRegex);
    const matchedPw = pw.match(pwRegex);

    if (matchedEmail === null) {
        alert("email 형식에 맞게 입력해 주세요.");
        emailRef.current.focus();
        return true;
    }

    if (matchedPw === null) {
        alert("비밀번호는 8자리 이상 영문자, 숫자, 특수문자 조합이어야 합니다.");
        pwRef.current.focus();
        return true;    
    }
};

const handleRegister = () => {
    if (validInputs()) {
        return;
    }

    createUserWithEmailAndPassword(authService, email, pw)
        .then(() => {
            setEmail("");
            setPw("");
            goBack();
        })
        .catch((error) => {
            if (error.message.includes("already-in-use")) {
                alert("이미 사용 중인 아이디입니다.")
            }
        });
};

const handleLogin = () => {
    if (validInputs()) {
        return;
    }

    signInWithEmailAndPassword(authService, email, pw)
        .then(() => {
            setEmail("");
            setPw("");
            goBack();
        })
        .catch((error) => {
            if (error.message.includes("user-not-found")) {
                alert("회원이 아닙니다. 회원가입을 먼저 진행해 주세요.")
            }
            if (err.message.includes("wrong-password")) {
                alert("비밀번호가 틀렸습니다.");
            }
        });
};

너무 쉽잖아... 내가 firebase authservice 없이 로그인, 회원가입 구현하기 위해 애썼던 시간들이 스쳐 지나간다🤧

 

 

react-native-ratings

const [rating, setRating] = useState(0);

<Rating
    onFinishRating={getRating}
    startingValue={0}
    ratingCount={10}
    imageSize={25}
    tintColor="lightgray"
    style={{
        alignItems: "flex-start"
    }}
/>

공식 문서 https://www.npmjs.com/package/react-native-ratings

 

 

Date.now()로 만든 데이터 변환하기

createdAt: Date.now() // 1672898991095

{new Date(review.createdAt).toLocaleDateString("kr")}

참고 https://carrotweb.tistory.com/159

 

 

버튼을 클릭했을 때 params를 넘기는 방법 두 가지

// params 보낼 때
const ReviewCard = ({review}) => {

    const goToReview = () => {
        navigate("Review", {
            review,
            from: "Detail"
        });
    };
    // 한 눈에 보면 이렇게 생김. params 넘기는 중!
    navigation.navigate("ReviewEdit", { review, from })

    return (
        <Column onPress={goToReview}>
         ...생략...
        </Column>
      );
};

// params 받을 때
const Review = ({ route: { params: { review, from }}}) => {
}

Stacks 안에 있는 페이지들 사이에서 이동할 때

 

// params 보낼 때
<MovieContainer 
    onPress={() => navigate("Stacks", {
        screen: "Detail",
        params: { movieId: movie.id }
    })}
>

// params 받을 때
const Detail = ({ route: { params: { movieId }}}) => {
}

Tabs 안에 있는 Screen에서 버튼을 클릭해서 페이지 넘어갈 때

 

 

Firestore "The query requires an index." 에러 해결하기

const q = query(
    collection(dbService, "reviews"),
    orderBy("createdAt", "desc"),
    where("userId", "==", authService.currentUser?.uid)
);

Uncaught Error in snapshot listener:, FirebaseError: [code=failed-precondition]: The query requires an index. 

아마 쿼리에 "createdAt"과 "userId" 두 개의 조건을 설정해서 발생한 에러인 거 같다. 에러 메시지 옆에 클릭할 수 있는 링크가 하나 있는데 거기 들어가서 색인을 추가했더니 문제가 해결되었다.

참고 https://jason-api.tistory.com/41

 

 


회고

리액트 네이티브에 점점 익숙해지고 있다. stack이나 tab 구조도 어느 정도 이해가 되고 firebase로 대이터 추가하고 가져오는 방법도 공부했다. 내일부터 프로젝트 시작이라 오늘 저녁에는 팀회의 가지면서 어떤 컨셉으로 앱을 만들면 좋을지 얘기도 나눴다. 근데 쿼리 처음으로 공부하는데 생각보다 어려운 거 같다..😅

 

댓글