React-apollo: FetchMore result not passed to component if using union types

Created on 6 Apr 2017  路  10Comments  路  Source: apollographql/react-apollo

I wasn't quite sure if this belongs here or apollo-client as it somewhat relates to https://github.com/apollographql/apollo-client/issues/1523, however my fetchMoreResult returns the expected results. The issue is that the new result isn't passed into my component if I'm using Union types.

Intended outcome:
I'm attempting to use fetchMore with limit/offset. My query contains union types. I'd expect that after I concat fetchMoreResult with my previous result it would be passed into my component.

Actual outcome:
The new result isn't passed into the component. I have an author prop which indicates the author of the post. It can be an individual user or a company (each entity has unique properties). If I remove the author prop from my GetPosts query, everything works as expected with the newResult being passed into my component. This leads me to believe that the union type is the culprit.

How to reproduce the issue:

import React from 'react';
import { View, Text } from 'react-native';
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';

const GetPosts = gql`query GetPosts($limit: Int, $offset: Int) {
  posts(limit: $limit, offset: $offset) {
    _id
    message
    createdAt
    scheduledAt
    authorType 
    author {
      ... on User {
        _id
        avatarUrl
        profile {
          firstName
          lastName
        }
      }
      ... on Company {
        _id
        name
        logo80
      }
    }
  }
}
`;

class TestComponent extends React.Component {
  render() {
    // I would expect this to log the new props after press "loadMore"
    console.log(this.props)
    return (
      <View style={{marginTop: 40}}>
        <Text onPress={this.props.loadMore}>Push me</Text>
      </View>
    )
  }
}

export default graphql(GetPosts, {
  options: () => ({
    variables: {
      offset: 0,
      limit: 1
    }
  }),
  props: ({ ownProps, data }) => {
    return {
      ...ownProps,
      posts: data.posts,
      loadMore: () => {
        return data.fetchMore({
          variables: {
            offset: 1,
            limit: 1
          },
          updateQuery: (previousResult, { fetchMoreResult }) => {            
            if (!fetchMoreResult) {
              return previousResult;
            }
            const newResult = Object.assign({}, previousResult, {
              posts: [...previousResult.posts, ...fetchMoreResult.posts]
            });
            // This logs exactly what I expect it to log, but it isn't re-rendered into the component.
            console.log(newResult);

            return newResult;
          },
        })
      }
    };
  }
})(TestComponent);

Version

  • apollo-client@^1.0.1
  • react-apollo@^1.0.0
  • graphql-tag@^2.0.0

Most helpful comment

@helfer I don't think using the fragment matcher should the definitive solution.
This should be transparent to the user in my opinion. :/

All 10 comments

Thanks @rpylipow! Could you provide a reproduction using the error template? That way we could step through the code and find out where the bug happens.

Please take a look at https://github.com/rpylipow/react-apollo-error-template/tree/bug/union

I have two queries: One using union types and one not using union types.

If you use the union types query then press the "load more" button (which calls fetchMore), the result of fetchMoreResult is correct, but it doesn't re-render into the component. If you use the non-union type query, everything re-renders correctly.

Passing trough the same thing:

const SearchQuery = gql`
    query Search($query: String!, $endCursor: String) {
        search(query: $query, first: 3, after: $endCursor) {
            pageInfo {
                hasNextPage
                endCursor
            }
            edges {
                node {
                    ... on EventResult {
                        id
                        name
                        url
                        startDate
                        endDate
                        countryCode
                        cityName
                        locationName
                    }

                    ... on CityResult {
                        id
                        name
                        url
                        countryCode
                    }

                    ... on LocationResult {
                        id
                        name
                        url
                        cityName
                        countryCode
                    }

                    ... on ArtistResult {
                        id
                        name
                        url
                        avatar
                    }
                }
            }
        }
    }
`

const SearchContainerWithData = graphql(SearchQuery, {
    props: ({data: {search, loading, fetchMore, refetch}}) => {

        // This is not triggered 
        console.log('This is not logged when fetchMore');

        const results = search && search.edges.map(({node}) => node)
        const hasNextPage = search && search.pageInfo.hasNextPage
        const endCursor = search && search.pageInfo.endCursor

        return {
            results, hasNextPage, loading, endCursor,
            searchFor: refetch,
            loadMore: (variables) => {
                return fetchMore({
                    variables,
                    updateQuery: (previousResult, {fetchMoreResult}) => {
                        debugger
                        const newEdges = fetchMoreResult.search.edges
                        const pageInfo = fetchMoreResult.search.pageInfo

                       // return here is exactly what I expect
                       return {
                            search: {
                               edges: [...previousResult.search.edges, ...newEdges],
                               pageInfo,
                            },
                       }
                   }
               })
            }
        }
    },
    options: ({query}) => {
        return {
            variables: {
                query
            }
        }
    }
})(SearchContainer)

I receive the results that I expect, fetchMore method return what I'm expecting but apollo-client doesn't

The fetch more returns exactly what I expect, the apollo client redux store is updated with the data from fetchMore, but the rerender is not triggered.

same here, but everything works fine with apollo-client 0.9.0 and below

I think this is the same underlying issue as https://github.com/apollographql/apollo-client/issues/1555, using the fragment matcher there should solve the problem.

However, since this seems to have worked before, I'll have to go back and check if I inadvertently removed the old fragment matcher somewhere.

Here is another reproduction: https://github.com/yury-dymov/react-apollo-error-template
I figured out that for 0.9.0 everything works fine: https://github.com/yury-dymov/react-apollo-error-template/tree/0.9.0

@yury-dymov can you check if the solution from #1555 solves your issue in the most recent version?

with [email protected] the problem is resolved using the solution from #1555.

It is noted the using [email protected] the problem is NOT resolved

@helfer I don't think using the fragment matcher should the definitive solution.
This should be transparent to the user in my opinion. :/

looks like this was solved by #1555 on AC?

Was this page helpful?
0 / 5 - 0 ratings