들어가기
select Photo, take Photo한 사진을 props로 받아서
caption과 함꼐 uploadPhoto Mutation을 실행하여,
사진을 DB에 저장하고, 뿌리는 screen
역시, file(picture, video)등을 다루는 부분은 어려우니 집중할것!
이어서 apollo.js의 httpServer를 UploadHttpServer로 바꿔주어야 한다.
apollo는 Upload라는 type을 이해하지 못하기 떄문이다.

npm i apollo-upload-client
https://www.npmjs.com/package/apollo-upload-client

file upload를 하기 위해서는 ReactNativeFile을 시용해야 하는데,
ReactNativeFile을 사용하기 위해거 apollo-upload-client를 이용한다

https://www.apollographql.com/docs/react/api/link/apollo-link-error

import React, { useEffect } from 'react'
import { gql, useMutation } from '@apollo/client'
import styled from 'styled-components/native'
import DismissKeyboard from '../components/DismissKeyboard'
import { useForm } from 'react-hook-form'
import { ActivityIndicator, TouchableOpacity } from 'react-native'
import { ReactNativeFile } from 'apollo-upload-client'   
///1)ReactNativeFile import할 것!!

const UPLOAD_PHOTO_MUTATION = gql`
///2)backend의 uploadPhoto mutation을 불러준다.
///return 받는 부분은 photo라, seeFeed처럼, 
///seeFeed로 불러지는 것과 같이 불러들인다.

  mutation uploadPhoto($file: Upload!, $caption: String) {
    uploadPhoto(file: $file, caption: $caption) {
      id
      user {
        id
        username
        avatar
      }
      file
      caption
      likes
      commentNumber
      createdAt
      isMine
      isLiked
      comments {
        id
        user {
          username
          avatar
        }
        payload
        isMine
        createdAt
      }
    }
  }
`

const Container = styled.View`
  flex: 1;
  background-color: black;
  padding: 0px 20px;
`
const Photo = styled.Image`
  flex: 0.5;
  height: 450px;
`
const CaptionContainer = styled.View`
  margin-top: 10px;
`
const Caption = styled.TextInput`
  background-color: white;
  color: black;
  padding: 10px 20px;
  border-radius: 100px;
`
const HeaderRightText = styled.Text`
  color: skyblue;
  font-size: 16px;
  font-weight: 600;
  margin-right: 7px;
`

export default function UploadForm({ route, navigation }) {
///3)selectPhoto 및 takePhoto에서 보내주는 사진을 받기 위해서
///route와 navigation을 불러들인다.

  console.log(route.params.file)
  const updateUploadPhoto = (cache, result) => {
    const {
      data: { uploadPhoto },  ///backend의 uploadPhoto mutaiton 실행 후 data받음.
    } = result
    if (uploadPhoto.id) {   ///data를 잘 받았으면, cache를 modify한다.
      cache.modify({        ///seeFeed는 ROOT_QUERY에 있기 떄문에, ROOT_QUERY를
        id: 'ROOT_QUERY',   ///modify한다.
        fields: {           ///fileds는 prev를 받아서, uploadPhoto data와
          seeFeed(prev) {    ///이전 data, 즉, ...prev 를 같이 불러준다.
            return [uploadPhoto, ...prev]   ///seeFeed의 offset부분은
          },                                ///apollo.js에서 설정해줘서   
        },                                  ///offsetLimitPagination으로
      })                                     ///신경안써도됨.
      navigation.navigate('Tabs')     ///마지막으로, cache.modify실행후,
    }                                  ///Tabs Nav로 이동시킨다~
  }
  ///5)useMutation의 update함수를 만든다.
  
  const [uploadPhotoMutation, { loading, error }] = useMutation(
    UPLOAD_PHOTO_MUTATION,
    {
      update: updateUploadPhoto,
    }
  )
///4)useMutaiton을 불러들여서 uploadPhotoMutation을 코딩한다.
///실시간 엡뎃을 위해서, update:updateUploadPhoto를 만들고,
///updateUploadPhoto 함수를 만든다. variables는 onValid에 넣는다.

  const HeaderRight = () => (
    <TouchableOpacity onPress={handleSubmit(onValid)}>
      <HeaderRightText>Next</HeaderRightText>
    </TouchableOpacity>
  )
  ///11)header의 right부분에 Next버튼을 만들어서, 이 버튼을 누르면,
  ///onValid가 실행되게 한다. onPress={handleSubmit(onValid)}로~

  const HeaderRightLoading = () => (
    <ActivityIndicator size="small" color="white" style={{ marginRight: 10 }} />
  )
  ///12)사진 upload시 loading되는 시간동안, ActivityIndicator가 나오게 하는 것.

  const { register, handleSubmit, setValue } = useForm()
  ///6)caption과 file을 다룰 useForm을 만든다.

  useEffect(() => {
    register('caption')
  }, [register])
  ///7) Caption부분의 onChangeText부분의 text를 받기 위해,
  ///useEffect를 이용한다, register('caption) , [register] 로~

  useEffect(() => {
    navigation.setOptions({
      headerRight: loading ? HeaderRightLoading : HeaderRight,
      ...(loading && { headerLeft: () => null }),
    })
  }, [loading])
  ///13)navigation.setOptions를 사용해서 header의 오른쪽 부분이
  ///사진 upload시 loading여부에 따라 다른 함수가 나오게 설정한다.
  /// ...(loading && { header.....})부분의 문법은 외울것.
  ///loading중일때, header의 left부분은 null로 설정한다는 뜻.

  const onValid = ({ caption }) => {
    const file = new ReactNativeFile({
      uri: route.params.file,
      name: `1.jpg`,
      type: 'image/jpeg',
    })
    ///9)filed을 만드는 문법, ReactNativeFile을 이용한다.
    ///uri는 route.params.file로 받고, name은 아무거나 설정해줘되 됨.
    ///type은 사진이니, image/jpeg로 받는다. 

    uploadPhotoMutation({
      variables: {
        file,
        caption,
        ///10)위에서 만든 file과 <Caption />에서 받은 caption을
        ///variables에 넣어서 uploadPhotoMutation을 실행시킨다.
      },
    })
  }
  ///8)onValid함수를 만든다.
  
  console.log(error)
  return (
    <DismissKeyboard> ///components에서 만든 Dismisskeyboard로 감싼다.
      <Container>
        <Photo resizeMode="contain" source={{ uri: route.params.file }} />
        ///Photo에 selectPhoto, takePhoto에선 보낸 사진을 route.params.file로 받는다.
        
        <CaptionContainer>
          <Caption  ///styled.TextInput으로 만든다.
            placeholder="Write a caption..."
            placeholderTextColor="rgba(0,0,0,0.5)"
            onChangeText={(text) => setValue('caption', text)} 
            ///caption에 입력 하는 글자 받을수 있게함, 이 부분 무조건 외울것
            
            onSubmitEditing={handleSubmit(onValid)}
            ///onSubmitEditing만들고 handleSubmut(onValid)를 만들고,
            ///onValid 함수를 만든다.
            
            returnKeyType="done"
            ///returnKeyType은 done으로 한다.
            
          />
        </CaptionContainer>
      </Container>
    </DismissKeyboard>
  )
}

2. apollo.js

import {
  makeVar,
  ApolloClient,
  InMemoryCache,
  createHttpLink,
} from '@apollo/client'  
import { setContext } from '@apollo/client/link/context'
import { offsetLimitPagination } from '@apollo/client/utilities'
import AsyncStorage from '@react-native-async-storage/async-storage'
import { onError } from '@apollo/client/link/error' 
///1)onError가 불려지는 from을 확인한다.

import { createUploadLink } from 'apollo-upload-client'
///2)createUploadLink가 불려지는 from을 확인한다.

export const isLoggedInVar = makeVar(false)
export const tokenVar = makeVar('')
export const TOKEN = 'token'

export const logUserIn = async (token) => {
  await AsyncStorage.setItem(TOKEN, token)
  isLoggedInVar(true)
  tokenVar(token)
}
export const logUserOut = async () => {
  await AsyncStorage.removeItem(TOKEN)
  tokenVar(null)
  isLoggedInVar(false)
}

const uploadHttpLink = createUploadLink({
  uri: 'https://18de-106-101-130-222.jp.ngrok.io/graphql',
})
///3)createHttpLink를 createUploadLink로 바꿔준다.
///나머지는 같음.

const authLink = setContext((_, { headers }) => {
  return {
    headers: {
      ...headers,
      token: tokenVar(),
    },
  }
})

const onErrorLink = onError((graphQLErrors, networkError) => {
  if (graphQLErrors) {
    console.log('GraphQL Error', graphQLErrors)
  }
  if (networkError) {
    console.log('Network Error', networkError)
  }
})
///4)통신시, netWorkError및 graphqlError를 알아내기 위해서 onErrorLink를 만들어줌.

export const cache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        seeFeed: offsetLimitPagination(),
      },
    },
  },
})

export const client = new ApolloClient({
  link: authLink.concat(onErrorLink).concat(uploadHttpLink),
  cache,
})
///5)HttpLink는 항상 맨 마지막에 오는 Link이기 때문에,
///항상, 맨 마지막에 넣는다.
profile
코딩하는초딩쌤

0개의 댓글