post-custom-banner


들어가기
seeFeed Query를 useQuery를 이용해서 data를 불러온 다음에
Flatlist로 뿌려줌, web에서는 map으로 뿌렸는데
app에서는 Flatlist로 뿌려줌.
Scrollview를 이용해도 되지만, 이어서 infiniteScroll까지 사용하기 위해서는
flatList를 사용해야함

1. screens/Feed.js (useQuery, Flatlist)


import React, { useEffect, useState } from 'react'
import { Alert, FlatList, Text, View } from 'react-native'
import { isLoggedInVar, logUserOut, TOKEN, tokenVar } from '../apollo'
import styled from 'styled-components/native'
import AsyncStorage from '@react-native-async-storage/async-storage'
import { gql, useQuery } from '@apollo/client' ///0.useQuery는 @apollo/client에서 불려짐~~
import useUser from '../useUser'
import ScreenLayout from '../components/ScreenLayout'
import Photo from '../components/Photo'

const Button = styled.TouchableOpacity`
  background-color: tomato;
  padding: 13px 10px;
  border-radius: 3px;
  width: 100%;
  opacity: ${(props) => (props.disabled ? '0.5' : '1')};
`

///1. FEED_QUERY를 만들어줌. web에서 만드는 것과 같음.

export const FEED_QUERY = gql`
  query seeFeed($offset: Int!) {
    seeFeed(offset: $offset) {
      id
      user {
        username
        avatar
      }
      file
      caption
      likes
      commentNumber
      createdAt
      isMine
      isLiked
      comments {
        id
        user {
          username
          avatar
        }
        payload
        isMine
        createdAt
      }
    }
  }
`
export default function Feed() {
  const User = useUser()
  console.log(User)
  
  ///2. 위에서 만든 FEED_QUERY를 useQuery로 불러줌.
  ///refetch, fetchMore, offset은 다음 post에서 설명할 예정임..
  
  const { data, loading, refetch, fetchMore } = useQuery(FEED_QUERY, {
    variables: {
      offset: 0,
    },
  })
  
  ///4)renderPhoto를 만들어줌, item으로 받는데 photo로 rename해줌.
  /// Photo component에 data를 넘겨주는데 {...photo}통으로 넘겨줌.
  ///원래는 
  ///     key={photo.id}
          id={photo.id}
          user={photo.user}
          file={photo.file}
          isLiked={photo.isLiked}
          likes={photo.likes}
          caption={photo.caption}
          commentNumber={photo.commentNumber}
          comments={photo.comments}
          photoId={photo.id}
  //////////이렇게 넘겨주어야 하나, 통으로 넘여줘도 상관없음.
  
  const renderPhoto = ({ item: photo }) => {
    return <Photo {...photo} />
  }
  const refresh = async () => {
    setRefreshing(true)
    await refetch()
    setRefreshing(false)
  }
  const [refreshing, setRefreshing] = useState(false)
  return (
    <ScreenLayout loading={loading}>
    	///3. ScreenLayout은 ActivitiIndicator사용을 위해서 만들어준 layout
        ///밑에서 참고바람
    
      <FlatList
        onEndReachedThreshold={0.05}
        onEndReached={() =>
          fetchMore({
            variables: {
              offset: data?.seeFeed?.length,
            },
          })
        }
        refreshing={refreshing}
        onRefresh={refresh}
        style={{ widht: '100%' }}
        showsVerticalScrollIndicator={false}
        data={data?.seeFeed}   ///1)useQuery를 이용해서 loading한 data를 불러줌.
        keyExtractor={(photo) => '' + photo.id}  ///2)ketExtractor를 설정함.
        renderItem={renderPhoto}  ///3)renderItem을 설정하고 위에 renderPhoto 함수만들어줌.
      />
    </ScreenLayout>
  )
}

2. components/ScreenLayout.js

data가loading될때, 뱅글뱅글 도는 아이콘 나오는거, 고거 설정해 주는것입니다.

import React from 'react'
import { ActivityIndicator, View } from 'react-native'

export default function ScreenLayout({ loading, children }) {
  return (
    <View
      style={{
        backgroundColor: 'black',
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
      }}
    >
      {loading ? <ActivityIndicator color="white" /> : children}
    </View>
  )
}

3. components/Photo.js (Feed.js에서 넘겨준 data 받음)

import React, { useEffect, useState } from 'react'
import styled from 'styled-components/native'
import { Image, Text, useWindowDimensions, View } from 'react-native'
import { useNavigation } from '@react-navigation/native'
import { TouchableOpacity } from 'react-native-gesture-handler'
import { Ionicons } from '@expo/vector-icons'
import Comments from '../screens/Comments'

const Container = styled.View``
const Header = styled.TouchableOpacity`
  padding: 10px;
  flex-direction: row;
  align-items: center;
`
const UserAvatar = styled.Image`
  margin-right: 10px;
  width: 25px;
  height: 25px;
  border-radius: 12px;
`
const Username = styled.Text`
  color: white;
  font-weight: 600;
`
const File = styled.Image``
const Actions = styled.View`
  flex-direction: row;
  align-items: center;
`
const Action = styled.TouchableOpacity`
  margin-right: 10px;
`
const Caption = styled.TouchableOpacity`
  flex-direction: row;
`
const CaptionText = styled.Text`
  color: white;
  margin-left: 20px;


`
const Likes = styled.Text`
  color: white;
  margin: 7px 0px;
  font-weight: 600;
`
const ExtarContainer = styled.View`
  padding: 10px;
`

function Photo({ id, user, caption, file, isLiked, likes }) {
	///1. Feed page에서 {...photo}로 보내준 data를 위와 같이 받음.

  const navigation = useNavigation()  
    ///user, likes클릭시 해당 page로 이동하기 위해서 사용
    
  const { width, height } = useWindowDimensions()
  	///화면의 width, height 값을 뽑아내기 위해서 사용
  
  const [imageHeight, setImageHeight] = useState(height - 450)
  useEffect(() => {
    Image.getSize(file, (width, height) => {
      setImageHeight(height / 3)
    })
  }, [file])
  ///Image.getSize는 사진 본연의 크기의 값을 get함. 
  ///위애서는 사진 본연의 크기의 값을 /3으로 해서 phone에 뿌려줌.
  
  return (
    <Container>
      <Header onPress={() => navigation.navigate('Profile')}>
      	///username과 userAvatar 클릭시, Profile page로 이동
        ///data 받아주는 부분, uri:user.avatar, user.username 주의깁게 볼것!
        ///resizeMode는 cover아니면, contain
        ///server에 있는 사진은 require, db에 있는 사진은 uri
        <UserAvatar resizeMode="cover" source={{ uri: user.avatar }} />
        <Username>{user.username}</Username>
      </Header>
      <File
        resizeMode="cover"
        style={{ width, height: imageHeight }}
        source={{ uri: file }}
      />
      ///사진 크기 설정
      
      <ExtarContainer>
        <Actions>
          <Action>
            <Ionicons
              name={isLiked ? 'heart' : 'heart-outline'}
              color={isLiked ? 'tomato' : 'white'}
              size={22}
            />
            ///isLiked에 따라서 하트 모양 설정해줌.
            
          </Action>
          <Action onPress={() => navigation.navigate('Comments')}>
            <Ionicons
              name="chatbubble-ellipses-outline"
              color="white"
              size={22}
            />
          </Action>
        </Actions>
        <TouchableOpacity onPress={() => navigation.navigate('Likes')}>
          <Likes>{likes === 1 ? '1 like' : `${likes} likes`}</Likes>
        </TouchableOpacity>
        <Caption onPress={() => navigation.navigate('Profile')}>
          <Username>{user.username}</Username>
          <CaptionText>{caption}</CaptionText>
        </Caption>
      </ExtarContainer>
    </Container>
  )
}

export default Photo
profile
코딩하는초딩쌤
post-custom-banner

0개의 댓글