Close

Sign up to Gadget

Sign Up

Gadget React Bindings

Mohammad Hashemi
November 30, 2021

React is a great tool for building amazing web and mobile experiences. Today, we’re excited to announce a set of rich, strongly typed React hooks for working with your Gadget applications.

The React bindings work with the existing generated Javascript client that we provide for each of your projects, and add a natural way to run queries and mutations within React components. The React bindings join an already impressive roster of features supported by the client, including strong types based on your application schema, automatic authentication, data hydration and caching.

Let’s take a look at an example. Say we have a Blog application with a <inline-code>Post<inline-code> model. Using the existing Javascript client, we could get a list of posts like so:


In a React application however, we need a data fetching pattern that provides a better user experience by displaying a spinner when the data is loading, or displaying errors when a network call fails. Using the Gadget generated client, our code becomes:


The <inline-code>@gadgetinc/react<inline-code> package shares the same strong TypeScript types that the generated Javascript client supports as well, allowing you to explicitly select fields from your models and get autocomplete and warning squiggles in your editor. It also supports sorting, filtering, and traversing relationships across models, so you can select fields of the Post’s author, or its images, or sort to get the most recent posts right in your call to <inline-code>useFindMany<inline-code>.

For example, we can make an explicit selection when querying the posts list, and we’ll get an error if we try to access a property we didn’t select:

And, with or without an explicit selection, we get editor autocomplete when accessing fields of the selected records:

The <inline-code>@gadgetinc/react<inline-code> package includes hooks for everything you can do in with your app's API, like <inline-code>useFindOne<inline-code>, <inline-code>useAction<inline-code>, <inline-code>useBulkAction<inline-code>, and more.

For more examples on what you can build using the Shopify Connection, check out these additional blog posts:

Keep reading to learn about how it's built

Under the hood

The Gadget React bindings use the same featureful GraphQL API that Gadget generates for each application. Both for React and the API client, we use the popular and battle-tested <inline-code>urql<inline-code> GraphQL client. We’ve been using <inline-code>urql<inline-code> in production for many years, and appreciate its robustness and extensibility. It is performant, well maintained, has a small code footprint, and is designed with extensibility in mind. We also really like the default caching mode for urql (document caching), which works great out of the box and requires no extra developer work to set up.

Making <inline-code>@gadgetinc/react<inline-code> feel great to use means having extensive TypeScript support, and like the base API client, this is tricky business to deliver out of the box. The schema for your application is completely controlled by you, and we'd rather your API client really feel like yours as well, which means its types should be strict and specific to your app. Because each call to a <inline-code>@gadgetinc/react<inline-code> hook can pass a different or dynamic <inline-code>select<inline-code> parameter, the return type is generic over the selection. For both <inline-code>@gadgetinc/react<inline-code> and the base API client, Gadget isn't generating simple static types based on your schema, it's generating an entire representation of the schema for the higher-kinded types to map over for each concrete call.

Because all this type-time stuff is just types, we're able to "export" the base generated types at type-time from the base API client, and "import" them in the react package. The trick is not making you write actual `import` statements for these types, and instead allowing them to be inferred from a simple <inline-code>useFindOne(api.widget)<inline-code> call or similar. If we didn't do this, you'd have to write overly verbose code like <inline-code>useFindOne<Widget>(api.widget)<inline-code> or worse.

So, in order to make the types inferable, we had to put them in a spot on the passed in argument to the function, which is that property of the base API client, <inline-code>api.widget<inline-code>. The types have to be reachable with TypeScript's type access operator `SomeType["someProperty"]`, and so, we did just that! <inline-code>@gadgetinc/react<inline-code> works in collaboration with the base API client by accessing type-time only properties exported on each model's property on the base API client. These type-time only exports don't add any bundle size to the compiled JS, but provide quick and easy inference without extra imports or parameters.

These virtual properties were a strange pattern we hadn’t seen before, but ended up working great, and are a testament to the power of TypeScript’s flexibility and out of band approach.

Learn more

For more information, see the docs for using React with an example project here. You can also visit the gadget-inc/examples repo for additional examples.