Skip to content

Infinite loop caused by Type Policy with read function #7028

@timothyarmes

Description

@timothyarmes

My app has a User type for which I've defined a type policy:

    User: {
      fields: {
        createdAt:  { read: (date) => date ? parseISO(date) : null },
      },
    },

I also have a query to fetch the currently logged in user, and a HOC is used to fetch that user and render sub components.

const GET_SELF = gql`
  query me {
    me {
      ...userFragment
      prefs { ...userPrefsFragment }
    }
  }
  
  ${Users.userFragment}
  ${Users.userPrefsFragment}
`;

const UserProvider = ({ render }) => {
  const { error, loading, data: { me } = {}, refetch, subscribeToMore } = useQuery(GET_SELF, { fetchPolicy: 'cache-first' });

  <snip>

  if (loading) return <Loading />;
  return render(me);
}

Now, in a subcomponent I'm running a query which fetches a set of users:

export const GET_MISSION = gql`
  query mission($id: ID!) {
    mission(_id: $id) {
      users { ...userDisplayFragment }
    }
  }

 ${userDisplayFragment}
`;

When that query returns the currently logged-in user as part of the users list, the code enters into an infinite loop.

The fields returned for the currently logged in user are merged into the user object in the cache. Despite the fact that these fields have not changed the value of that user object, this causes the 'me' query to rerun, which reruns the 'mission' query above, which leads to the infinite loop.

However, if I remove the createdAt field from the Type Policy for the User object then everything works.

I thought that I be able to work around the issue be returning the exact same parsed date object when the date doesn't change:


class DatePolicy {
  constructor() {
    this.prevDate = null;
    this.prevParsedDate = null;
  }

  read = (date) => {
    if (date !== this.prevDate) {
      this.prevDate = date;
      this.prevParsedDate = date ? parseISO(date) : null;
    }

    return this.prevParsedDate;
  }
}

And using this class in the Type Policy:

User: {
  fields: {
    createdAt: new DatePolicy(),
  },
},

however that doesn't solve the problem.

If however the read function returns the object unchanged then the loop is avoided:

read = (date) => date

So, it seems that when a read function that returns a value other than that passed in will force the query to re-run.

Versions

System:
OS: macOS 10.15
Binaries:
Node: 12.13.1 - ~/.nvm/versions/node/v12.13.1/bin/node
Yarn: 1.19.2 - /usr/local/bin/yarn
npm: 6.12.1 - ~/.nvm/versions/node/v12.13.1/bin/npm
Browsers:
Chrome: 85.0.4183.102
Firefox: 77.0.1
Safari: 13.0.2
npmPackages:
@apollo/client: ^3.2.0 => 3.2.0
apollo-link-logger: ^1.2.3 => 1.2.3
apollo-server-express: ^2.17.0 => 2.17.0

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions