Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Built in react client #13

Closed
remorses opened this issue Jun 21, 2020 · 8 comments
Closed

[RFC] Built in react client #13

remorses opened this issue Jun 21, 2020 · 8 comments

Comments

@remorses
Copy link
Owner

remorses commented Jun 21, 2020

To see an example possible client usage check out this code

Using genql with generic react fetching libraries like react-query and swr is not optimal:

  • the useQuery and useSwr functions use genetics to get the result types, this way typescripts loses auto completion for the request fields when you use an inline function (because of a cyclic generic inference, I thought my vscode was buggy but it behaves this way by design)
  • there is no clear way to make mutations like in apollo, where you can defer the mutation execution (when a button is clicked for example)
  • there is currently no way to handle subscription using hooks, (like useSubscription from urql does)

This is why I want to build a native react client based on swr, the exported functions will be similar to the apollo ones, but caching, polling, etc will be handles by swr

The exported functions will be

  • useQuery, tiny wrapper around useSwr
  • useMutation, does not cache results, can be deferred unit an execute function
  • useSubscription, similar to useSubscription from urql, gets a reducer argument that aggregates subsequent subscription results into an accumulator

This client will be generated when you add a --react flag, it will require an swr peer dependency

@remorses remorses pinned this issue Jun 21, 2020
@remorses
Copy link
Owner Author

Let me know if you have other ideas for the client

@remorses remorses changed the title [RFC] Add native react integration [RFC] Native react client Jun 21, 2020
@remorses remorses changed the title [RFC] Native react client [RFC] Built in react client Jun 23, 2020
@remorses
Copy link
Owner Author

remorses commented Jun 23, 2020

Another great addition would be usePaginatedQuery, an hook that contains pagination logic that works with types that implement the Relay Connection spec

Given that i completely control the queries i can make assumptions about queries types and manipulate them accordingly

This way the hook function can contain the pagination logic that changes the query arguments (and is also type safe)

It would look something like

const Page = () => {
    const { items, nextPage } = usePaginatedQuery(
        { itemsPerPage: 10 },
        ({ first, after }) => {
            return {
                users: [
                    {
                        first,
                        after,
                    },
                    {
                        edges: {
                            ...everything,
                        },
                        pageInfo: {
                            ...everything,
                        },
                    },
                ],
            }
        },
    )

    return (
        <div>
            {items.map((x) => x.name)}
            <button onClick={nextPage}>more</button>
        </div>
    )
}

@dhmacs
Copy link
Contributor

dhmacs commented Jul 7, 2020

  • the useQuery and useSwr functions use genetics to get the result types, this way typescripts loses auto completion for the request fields when you use an inline function (because of a cyclic generic inference, I thought my vscode was buggy but it behaves this way by design)

IMO this is not a big issue since it can just be avoided by declaring a function not inline, and it's what I naturally do when I follow React Query docs, e.g.

import { useQuery } from "react-query";
import QueryCacheKey from "@/constants/queryCacheKeys";
import useSdk from "@/api/sdk";

export default function useItem(id?: string) {
  const sdk = useSdk();

  const getItem = (key: string, id?: string) => {
    return sdk.query({
      item: [
        {
          id,
        },
        {
          id: true,
          name: true,
          description: true,
          code: true,
          created_at: true,
          updated_at: true,
          property_values: [
            {},
            {
              item_property_id: true,
              text: true,
              number: true,
            },
          ],
        },
      ],
    });
  };

  return useQuery([QueryCacheKey.Item, id], getItem, { enabled: !!id });
}

export type ItemResult = ReturnType<typeof useItem>["data"];
  • there is no clear way to make mutations like in apollo, where you can defer the mutation execution (when a button is clicked for example)

What do you mean here by "defer the mutation execution"?

  • there is currently no way to handle subscription using hooks, (like useSubscription from urql does)

I think that subscriptions need more thinking than normal queries. For example, if you have a paginated query, how do you make it realtime with subscription? When using Apollo I used to just swap useQuery with useSubcription, but this is less than ideal, as if just one item updates, the entire page is fetched. And it's also not very clear what happens if something updates out of the range that you are subscribing to.

For my apps, I tend to use subscriptions as a notifier (e.g. I listen to item updates ordered by created_at with limit 1). When I receive the notification, I can decide to either invalidate the cache for my paginated query, or just update the piece of cache that is affected using subscription data. In this scenario I don't really need to cache the subscription itself, I just use it as an event notifier to know when something changes in realtime.

@remorses
Copy link
Owner Author

remorses commented Jul 9, 2020

What do you mean here by "defer the mutation execution"?

There is an example here

const [execute, { loading, error, result: data }] = useMutation(


For example, if you have a paginated query, how do you make it realtime with subscription?

There is an example of useSubscription here

const { result: data, loading, error } = useSubscription(

Above i don't pass any reducer function, but you could get so it works just like a last update notifier

If you instead pass a reducer that accumulates the subscription results you could get a stream of notification for example

    const { data, loading, error } = useSubscription(
        {
            onNewMessage: {
              message: true,
           }
        },
       (acc: string[], item) => [...acc, item.message],
    )

I think this is pretty extensible, do you have any other ideas for useSubscription?

I am still trying these concepts on my application to test all the different use cases, i am open to changes

@dhmacs
Copy link
Contributor

dhmacs commented Jul 9, 2020

There is an example here

const [execute, { loading, error, result: data }] = useMutation(

I think I'm not getting it. What problem is this trying to solve that something like React Query has? The syntax looks almost identical to me

There is an example of useSubscription here

const { result: data, loading, error } = useSubscription(

Above i don't pass any reducer function, but you could get so it works just like a last update notifier

If you instead pass a reducer that accumulates the subscription results you could get a stream of notification for example

    const { data, loading, error } = useSubscription(
        {
            onNewMessage: {
              message: true,
           }
        },
       (acc: string[], item) => [...acc, item.message],
    )

I think this is pretty extensible, do you have any other ideas for useSubscription?

I am still trying these concepts on my application to test all the different use cases, i am open to changes

How do you update the cache when you receive new data? Are you planning to expose swr mutate function?

@jgoux
Copy link

jgoux commented Jul 18, 2020

Why do you consider general purpose libraries instead of a more specialized one like urql?

swr and react-query are awesome, but they aren't build around graphql.

The main advantage I see picking urql is that the cache is built around the graphql schema, so you have a lot of optimizations and features for free. Also it's very flexible and extendable with its exchange system.

@remorses remorses unpinned this issue Nov 11, 2020
@jgoux
Copy link

jgoux commented Jan 15, 2022

Sorry for the double post but we have now a common type standardized around client consumption : https://github.com/dotansimha/graphql-typed-document-node

I think the best course of action would be to end with a TypedDocumentNode, this is what tql is doing and it seems to work great! So we can build the query using genql syntax but defer the execution to any client compatible with TypedDocumentNode!

@thdxr
Copy link

thdxr commented Mar 24, 2022

^ I'm also interested in this - we're looking for a typesafe query client that can generate TypedDocumentNode so it can be used with any client. Specifically looking to use urql

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants