홈화면 구성

1. feed query만들기

2. backend에 token보내기

apolloClinetrequest함수 알아보기

App.js

 //create apolloClient
      const client = new ApolloClient({
        cache,
        request: async operation => {
          const token = await AsyncStorage.getItem("jwt");
          return operation.setContext({
            headers: { Authorization: `Bearer ${token}` }
          });
        },
        ...apolloClientOptions
      });

request는 opration을 인자로 갖는다. 토큰을 로컬스토리지에서 꺼내서 매번 사용자가 요청을 할 경우 headers에 붙혀서 보낸다.

apollo Docs

3. 당겨서 새로고침

**scrollView **: 대량의 요소를 사용하는 경우에는 적합하지 않음.

flatList : 성능이 좋은 스크롤 view를 제작할 때 사용하는 것이 적합함.

두가지 중에서 상황에 맞는 것을 골라 사용하면 됨. ScrollView를 사용.

스크롤 당김 -> UseQueryrefetch가 동작해서 backend에서 새로운 data를 불러옴.

3-1 useQuery refetch

apollo Docs

1564747834044

3-1. refreshControl

reactNative Docs

아래와 같이 ScrollView의 속성인 refreshControl을 사용해야하고 refreshControl의 파라메터로는 <RefreshControl>를 주어야 합니다.

1564747924787

render() {
    return (
      <ScrollView
        refreshControl={
          <RefreshControl
            refreshing={this.state.refreshing}
            onRefresh={this._onRefresh}
          />
        }
        ...
      />
    );
  }

1564748017236

RefreshControl의 prop중에서는 위의 두개를 사용할 것 입니다.

최종 코드

export default () => {
  const [refreshing, setRefreshing] = useState(false); // 당겨서 새로고침에 사용될 state
  const { loading, data, refetch } = useQuery(FEED_QUERY);
  const refresh = async () => {
    try {
      setRefreshing(true);
      await refetch();
    } catch (error) {
      console.log(error);
    } finally {
      setRefreshing(false);
    }
  };
  console.log(loading, data);
  return (
    <View>
      {loading && <Loader />}
      {!loading && (
        <ScrollView
          refreshControl={
            <RefreshControl refreshing={refreshing} onRefresh={refresh} />
          }
        >
          <Text>home</Text>
        </ScrollView>
      )}
    </View>
  );
};

4. Post Component 만들기

4-1. Proptypes 체크하기

React proptypes Docs

  • 객체일 경우

    1564749621351

    optionalObjectWithShape: PropTypes.shape({
        color: PropTypes.string,
        fontSize: PropTypes.number
      }),
    
  • 배열

    // An array of a certain type
      optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
    
  • 여러개 중의 하나

    1564749645911

 optionalEnum: PropTypes.oneOf(['News', 'Photos']),

Post의 Proptypes를 지정하기 위해서는 shape이 필요함.

Post.user => shape

Post.comments => arrayof

Post.files => arrayof

 files: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.string.isRequired,
      url: PropTypes.string.isRequired
    })
  ).isRequired,

위 코드 처럼 배열일 경우 arrayOf를 사용하고 그 안에는 PropTypes.shape({})를 사용해야함.

Post.propTypes = {
  id: PropTypes.string.isRequired,
  location: PropTypes.string,
  caption: PropTypes.string.isRequired,
  likeCount: PropTypes.number.isRequired,
  isLiked: PropTypes.bool.isRequired,
  user: PropTypes.shape({
    id: PropTypes.string.isRequired,
    avatar: PropTypes.string.isRequired
  }).isRequired,
  files: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.string.isRequired,
      url: PropTypes.string.isRequired
    })
  ).isRequired,
  comments: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.string.isRequired,
      text: PropTypes.string.isRequired,
      user: PropTypes.shape({
        id: PropTypes.string.isLiked,
        username: PropTypes.string.isLiked
      })
    })
  ).isRequired
};

4-2. layout 구성

1564807993444

  • 1. Header

    1564807822446

    • Avatar
    • username
    • location
  • 2. Post
    • swiper
    • pagination
  • 3. Icon

    1564807905582

    • Heart
    • comment
  • 4. LikeCount

    1564807925456

  • 5. Caption

    1564807937379

  • 6. Comment

    1564807955407

    • writer
    • text
    • heart
  • 7. textinput

4-3. react-native-swiper

공식 github

게시물의 사진이 여러장일 경우 스와이프를 해주는 오픈소스입니다. 사용 방법이 굉장히 쉽기 때문에 사진을 사용할 때 자주 사용할 것 같습니다.

  • Install

    npm install --save react-native-swiper
    
  • Import

    사용할 파일의 위에 import를 해줍니다.

    import Swiper from 'react-native-swiper'
    
  • Usage

    impot한 Swiper태그 안에 이미지들을 넣어주기만 하면 됩니다.

     <Swiper
            style=
            showsButtons={false}
            showsPagination={true}
            paginationStyle=
          >
            {files.map(photo => (
              <Photo key={photo.id} source= />
            ))}
          </Swiper>
    

    그러면 속성에 대해서 알아보도록 하겠습니다.

  • Option

    ` showsButtons : swiper에 존재하는 버튼들을 모두 보여주는 속성 (true,false`)

    showsPagination : swiper에 존재하는 페이지 버튼 보여주는 속성 (true,false)

    1564806770636

    paginationStyle : pagination을 사진 밑에 두기 위해 bottom 값을 -20으로 사용

4-4. useState로 좋아요 토클 및 좋아요 갯수 변경

하트를 누를 때마다 좋아요를 토글해줘야 하는데 이게 실제 서버에 요청을 보내고 응답을 받은 후에 하트 색을 변경시키면 반응이 늦습니다. 그래서 사용하는 방식이 서버에서 응답이 올때 까지 기다리는 것이 아니라 임시 State를 만들어서 사용자가 좋아요 버튼을 누르면 (onPress) 빨간하트를 보여주는 방식을 사용하려고 합니다.

  • apollo-boost import

    import { gql } from "apollo-boost";
    
  • Mutation 만들기

    import { useMutation } from "react-apollo-hooks";
    // Mutaion & Query
    const TOGGLE_LIKE = gql`
      mutation toggleLike($postId: String!) {
        toggleLike(postId: $postId)
      }
    `;
      
    
  • Mutation 사용하기

    • handleLike 함수 만들기
    const handleLike = async () => { // 기다려야하기 때문에 async
        try {
          if (isLiked === false) {
            setIsLiked(true); // 좋아요 상태 
            setLikeCount(p => p + 1); // 좋아요 갯수 1증가 
          } else if (isLiked === true) {
            setIsLiked(false); // 좋아요 해제 
            setLikeCount(p => p - 1); // 좋아요 갯수 1감소 
          }
          await toggleLike(); // 사용자에게 보여준 후에 Mutation 요청 
        } catch (error) {
          console.log(e);
        }
      };
    
    • heart 아이콘에 onPress선언
    <Ionicons
                  color={isLiked ? Theme.redColor : Theme.blackColor}
                  name={
                    Platform === "ios"
                      ? isLiked
                        ? "ios-heart"
                        : "ios-heart-empty"
                      : isLiked
                      ? "md-heart"
                      : "md-heart-empty"
                  }
                  onPress={handleLike} // heart 아이콘의 클릭이벤트 
                  size={28}
                />