Skip to content
This repository has been archived by the owner on May 26, 2023. It is now read-only.

Add UpdateQuery for FetchMore #99

Merged
merged 8 commits into from
Jul 8, 2018

Conversation

jeffutter
Copy link
Contributor

@jeffutter jeffutter commented May 31, 2018

updateQuery for fetchMore

Pull Request Labels

  • feature
  • blocking
  • docs

fixes #79

This sort of works but the solution doesn't seem ideal. I'm posting it now to get some feedback. Does this seem like a reasonable approach?

Also, I plan to squash the PR once the solution seems agreed upon, but I would like to keep some of my past implementations for the time being until a solution is settled upon.

@ghost ghost added the blocking Prevents production or dev due to perf, bug, build error, etc.. label May 31, 2018
@jeffutter jeffutter mentioned this pull request May 31, 2018
5 tasks
}),
networkStatus: apolloData##networkStatus,
};
let make =
(
~variables: option(Js.Json.t)=?,
~updateQuery: option((Config.t, updateQueryOptions) => Config.t)=?,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is tricky, because Config.t is the initial Query, but you should be able to pass any query in the updateQuery?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if that is correct. My understanding is that updateQuery isn't the best name, it's not a new query it's just a function to combine the results from the initial query and a fetchMore query. The fetchMore query is the original query but can have new variables.

I'm getting this information from: https://www.apollographql.com/docs/react/features/pagination.html I may be interpreting it incorrectly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the link there is a mention:

note this is a different query than the one used in the Query component

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updateQuery shouldn't be a prop of the Query component. It's only a property of the config o ject passed to fetchMore

@jeffutter
Copy link
Contributor Author

updateQuery shouldn't be a prop of the Query component. It's only a property of the config o ject passed to fetchMore

Good point, I've updated the PR to reflect that.

note this is a different query than the one used in the Query component

Oh yeah, I just noticed that fetchMore takes an optional query parameter. That poses an interesting issue for typing that I'm not sure how to address. For now I'll continue ahead typing it the same as the original query and circle back once it is working.

It is closer to working now. The query executes but my combine function has some issue. Something is coming through as non-optional that I'm expecting to be optional. I'll keep plugging away.

@jeffutter
Copy link
Contributor Author

I'm pretty much stuck here. I can't get the typing to work out:

https://github.com/jeffutter/reason-apollo/blob/e4dfefdea61974e2bf78468e879e0dee7c56b12a/src/graphql-types/ReasonApolloQuery.re#L34-L39

The updateQuery function doesn't actually take Config.t's The Config.t types have stripped out the __type fields from the original payload and updateQuery is expected to return the raw objects that include the __type fields. I've tried losening the types all the way to just calling them Js.t({.}) but that isn't working either.

Any ideas?

@jeffutter
Copy link
Contributor Author

With the last commit, this "sort of" works. The function I pass in to fetchMore from my code gets: (Js.Json.t, {"fetchMoreResults": Js.Json.t}) I then have to do something like:

external unsafeFromJson : Js.Json.t => responseJS = "%identity";
external unsafeToJson : responseJS => Js.Json.t = "%identity";

...

let updateQuery = (previousResult, newResults) => {
  let previousResult = unsafeFromJson(previousResult);
  (
    switch (newResults##fetchMoreResult) {
    | Some(fetchMoreResult) =>
      let fetchMoreResult = unsafeFromJson(fetchMoreResult);
...

Where responseJS is a fleshed out version of what would be returned in Javascript.

It's not quite ideal but it works. Any suggestions on a better way would be appreciated.

@@ -8,7 +8,7 @@ type queryString;
* query string to the standard GraphQL AST.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As there are no actual changes in this file, just refmt, it should probably not be in this PR.

@@ -8,6 +8,23 @@ module Get = (Config: ReasonApolloTypes.Config) => {
| Loading
| Error(apolloError)
| Data(Config.t);
type updateQueryOptions = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you change this to

[@bs.deriving abstract]
  type updateQueryOptions = {
    fetchMoreResult: option(Js.Json.t),
    variables: Js.Json.t,
  };

you can remove the next type declaration as they are then the same.

And you can simplify the switch at line 122 to:

            switch (updateQuery) {
            | Some(updateQuery) =>
              Some(
                (
                  (previousResult, opts) => updateQuery(previousResult, opts)
                ),
              )

            | None => None
            }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems to have a small side effect. In the application code that is using this: opts##fetchMoreResult is no longer optional. It seems like it should be based on: https://www.apollographql.com/docs/react/features/pagination.html#numbered-pages shouldn't it?

@jeffutter
Copy link
Contributor Author

I think I've gotten the types sorted out here with the help of @ulrikstrid . Anyone else have any comments on this PR?

@jeffutter
Copy link
Contributor Author

Any thoughts on getting this merged?

Copy link
Contributor

@emmenko emmenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👌

@Gregoirevda
Copy link
Contributor

I added a commit containing:

  • An example
  • updateQueryOptions: variables are also optional (https://github.com/apollographql/apollo-client/blob/master/packages/apollo-client/src/core/ObservableQuery.ts#L44)
  • type updateQuery = (Js.Json.t, updateQueryOptions) => Js.Json.t; -> type updateQueryT = (Config.t, updateQueryOptions) => Js.Json.t; (not stripping out __typename)
  • fetchMoreOptions: object containing variables, query and updateQuery. updateQuery is the only required argument. I deleted query instead of assigning the same queryAST (will do that by default)
  • fetchMore type/function needs a unit at the end.

If you see any error, please comment!
Regarding type updateQueryT = (Config.t, updateQueryOptions) => Js.Json.t; :
It means you wont be able to access __typename of the first argument, but it makes it much easier to manipulate the data.
The return value has to be a Js.Json.t otherwise __typename isn't accepted, which unfortunately makes the return verbose!

I'm going to open a request in graphql_ppx, because apollo-client recommends adding __typename to Typescript and Flow. This will make it much easier to manipulate the previous and next GraphQL results

@jeffutter
Copy link
Contributor Author

@Gregoirevda I'm trying to update my application code to support these changes. I'm not sure about the use of

type updateQueryT = (Config.t, updateQueryOptions) => Js.Json.t;

The first argument isn't a Config.t it's a Js.Json.t. Using a Config.t the compiler makes me handle a bunch of optional values, however, what is actually getting passed in has no optionals, just JS values that may be null. As a result, I get index.js:2178 TypeError: Cannot read property 'whatever' of undefined because it unwraps an optional that isn't an optional and then tries to call a value on it.

@jeffutter
Copy link
Contributor Author

Any update on this?

[@bs.deriving abstract]
type updateQueryOptions = {
[@bs.optional]
fetchMoreResult: Config.t,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about fetchMoreResult? It should be a Js.Json.t no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I believe so.

@Gregoirevda
Copy link
Contributor

I asked for a feature in graphql_ppx for __typename fields support. This is related to this issue.
mhallin/graphql_ppx#39 (comment)

@Gregoirevda
Copy link
Contributor

Sorry for taking so long. I made the Config.t a Js.Json.t
Took the same logic in subscribeToMore in the Query component

@Gregoirevda Gregoirevda merged commit 7eef7dd into apollographql:master Jul 8, 2018
@jeffutter
Copy link
Contributor Author

Just tested this out on master, works great. Thanks!

@Gregoirevda
Copy link
Contributor

I could just not post a new big example project yesterday evening, will push asap

@Enalmada
Copy link
Contributor

Enalmada commented Dec 4, 2018

Can someone post how to make the reason-apollo swapi example actually update Apollo Store? Right now there is just a placeholder for it:

~updateQuery= (prev, _next) => {
   /* Update Apollo Store with [@bs.raw {||}] for now, since the type coming in is a generic Js.Json.t for now*/
   prev;
}

I see here in the apollo docs that the js version looks like this:

updateQuery: (prev, { fetchMoreResult }) => {
              if (!fetchMoreResult) return prev;
              return Object.assign({}, prev, {
                feed: [...prev.allPersons, ...fetchMoreResult.allPersons]
              });
            }

This kinda works but it is not handling the if (!fetchMoreResult) return prev; case. Could/should this be done better?

~updateQuery= (prev, next) => {   
    [%bs.raw {|
       Object.assign({}, prev, {allPersons: [...prev.allPersons, ...next.fetchMoreResult.allPersons]})
     |}]
}

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
blocking Prevents production or dev due to perf, bug, build error, etc..
Projects
None yet
Development

Successfully merging this pull request may close these issues.

updateQuery for fetchMore
5 participants