# `@gadgetinc/react`  The `@gadgetinc/react` package provides React hooks for calling your Gadget app's [auto-generated API](https://docs.gadget-canary.xyz/api/example-app/development/). ```tsx import { useFindMany, useAction } from "@gadgetinc/react"; // your app's auto-generated API client import { api } from "../api"; function WidgetDeleter() { // `useFindMany` executes a backend fetch to get a list of widgets from the backend. const [{ data, fetching, error }, refresh] = useFindMany(api.widget, { select: { id: true, name: true, }, }); // `useAction` sets up a mutation we can run to delete a specific widget when a user clicks a button const [_, deleteWidget] = useAction(api.widget.delete); if (fetching) return "Loading..."; return ( ); } ``` ## Key features  1. Rule-obeying hooks for reading and writing data from a backend that handle all request lifecycle and auth like `useFindOne`, `useAction`, and `useFetch` 2. Full type safety for inputs and outputs driven by each Gadget app's backend schema, including over dynamic selections 3. A full-featured, GraphQL-powered nested object selection system using the 4. Data hydrations that return useful objects like `Date` ## Installation  See the [installation instructions](https://docs.gadget-canary.xyz/api/example-app/development/installing) for your app's API. ### Provider Setup  Your React application must be wrapped in the `Provider` component from this library for the hooks to function properly. No other wrappers (like `urql`'s) are necessary. Gadget provisions applications with the required `` component already in place! If using the frontend hosting built into Gadget, no action is required as this step is already done. Example: ```tsx // import the API client for your specific application from your client package, be sure to replace this package name with your own import { ExampleAppClient } from "@gadget-client/example-app"; // import the required Provider object and some example hooks from this package import { Provider } from "@gadgetinc/react"; // instantiate the API client for our app const api = new ExampleAppClient({ authenticationMode: { browserSession: true } }); export const MyApp = (props: { children: React.ReactNode }) => { // wrap the application in the so the hooks can find the current client return {props.children}; }; ``` ### Client side vs Server side React  The `@gadgetinc/react` package is intended for use on the **client-side only**. The hooks provided by this package make requests from your app's frontend directly to your Gadget app's backend from the browser. If you want to use server-side rendering, you can use your framework's server-side data loader support and make imperative calls with your API client object. ## Hooks  ### `useFindOne()`  `useFindOne(manager: ModelFinder, id: string, options: SingleFinderOptions = {}): [{data, fetching, error}, refetch]` `useFindOne` fetches one record from your Gadget database with a given `id`. ```tsx import React from "react"; import { useFindOne } from "@gadgetinc/react"; import { api } from "../api"; export const ShowWidgetName = (props: { id: string }) => { const [{ data, fetching, error }, _refetch] = useFindOne(api.widget, props.id); if (fetching) return "Loading..."; if (error) return `Error loading data: ${error}`; return {data?.name}; }; ``` ##### Parameters  * `manager`: The model manager for the model you want to find a record of. Required. Example: `api.widget`, or `api.shopifyProduct` * `id`: The backend id of the record you want to find. Required. * `options`: Options for making the call to the backend. Not required, and all keys are optional. * `select`: A list of fields and subfields to select. See * `requestPolicy`: The `urql` request policy to make the request with. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `pause`: Set to true to disable this hook. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `suspense`: Should this hook suspend when fetching data. See * `select`: A list of fields and subfields to select. See * `requestPolicy`: The `urql` request policy to make the request with. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `pause`: Set to true to disable this hook. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `suspense`: Should this hook suspend when fetching data. See ##### Returns  `useFindOne` returns two values: a result object with the `data`, `fetching`, and `error` keys for inspecting in your React component's output, and a to trigger a refresh of the hook's data. * `data: GadgetRecord | null`: The record fetched from the backend. Is `null` while the data is being loaded, or if the record wasn't found. * `fetching: boolean`: A boolean describing if the hook is currently requesting data from the backend. * `error: Error | null`: An error from the client or server side, if encountered during the request. Will contain an error if the record isn't found by `id`. See the . `useFindOne` expects a record with the given id to be found in the backend database, and will return an error in the `error` property if no record with this id is found. `useFindOne` can select only some fields from the backend model with the option: ```tsx import React from "react"; import { useFindOne } from "@gadgetinc/react"; import { api } from "../api"; export const OnlySomeWidgetFields = (props: { id: string }) => { // fetch only the widget id and name fields const [{ data, fetching, error }, _refetch] = useFindOne(api.widget, props.id, { select: { id: true, name: true, }, }); return ( {data?.id}: {data?.name} ); }; ``` ### `useMaybeFindOne()`  `useMaybeFindOne(manager: ModelFinder, id: string, options: SingleFinderOptions = {}): [{data, fetching, error}, refetch]` `useMaybeFindOne` fetches one record from your Gadget database with a given `id`. ```tsx import React from "react"; import { useMaybeFindOne } from "@gadgetinc/react"; import { api } from "../api"; export const ShowWidgetName = (props: { id: string }) => { const [{ data, fetching, error }, _refetch] = useMaybeFindOne(api.widget, props.id); if (fetching) return "Loading..."; if (error) return `Error loading data: ${error}`; if (data) { return {data.name}; } else { return "No widget found"; } }; ``` `useMaybeFindOne` will return `data: null` and `error: null` if no record with the given `id` is found in the backend database. `useMaybeFindOne` otherwise behaves identically to , and accepts the same options. ### `useFindMany()`  `useFindMany(manager: ModelFinder, options: ManyFinderOptions = {}): [{data, fetching, error}, refetch]` `useFindMany` fetches a page of records from your Gadget database, optionally sorted, filtered, or searched. ```tsx import React from "react"; import { useFindMany } from "@gadgetinc/react"; import { api } from "../api"; export const ShowWidgetNames = () => { const [{ data, fetching, error }, _refetch] = useFindMany(api.widget); if (fetching) return "Loading..."; if (error) return `Error loading data: ${error}`; return ( ); }; ``` ##### Parameters  * `manager`: The model manager for the model you want to find a page of records for. Required. Example: `api.widget`, or `api.shopifyProduct` * `options`: Options for making the call to the backend. Not required and all keys are optional. * `select`: A list of fields and subfields to select. See the docs. * `filter`: A list of filters to limit the set of returned records. Optional. See the Model Filtering section in your application's API documentation to see the available filters for your models. * `search`: A search string to match backend records against. Optional. See the Model Searching section in your application's API documentation to see the available search syntax. * `sort`: A sort order to return backend records by. Optional. See the [sorting section](https://docs.gadget-canary.xyz/api/example-app/development/sorting-and-filtering#sorting) in your application's API documentation for more info. * `first` & `after`: Pagination arguments to pass to fetch a subsequent page of records from the backend. `first` should hold a record count and `after` should hold a string cursor retrieved from the `pageInfo` of the previous page of results. See the pagination section in your application's API documentation for more info. * `last` & `before`: Pagination arguments to pass to fetch a subsequent page of records from the backend. `last` should hold a record count and `before` should hold a string cursor retrieved from the `pageInfo` of the previous page of results. See the pagination section in your application's API documentation for more info. * `live`: Should this hook re-render when data changes on the backend. See the . * `requestPolicy`: The `urql` request policy to make the request with. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `pause`: Should the hook make a request right now or not. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `suspense`: Should this hook suspend when fetching data. See for more info * `select`: A list of fields and subfields to select. See the docs. * `filter`: A list of filters to limit the set of returned records. Optional. See the Model Filtering section in your application's API documentation to see the available filters for your models. * `search`: A search string to match backend records against. Optional. See the Model Searching section in your application's API documentation to see the available search syntax. * `sort`: A sort order to return backend records by. Optional. See the [sorting section](https://docs.gadget-canary.xyz/api/example-app/development/sorting-and-filtering#sorting) in your application's API documentation for more info. * `first` & `after`: Pagination arguments to pass to fetch a subsequent page of records from the backend. `first` should hold a record count and `after` should hold a string cursor retrieved from the `pageInfo` of the previous page of results. See the pagination section in your application's API documentation for more info. * `last` & `before`: Pagination arguments to pass to fetch a subsequent page of records from the backend. `last` should hold a record count and `before` should hold a string cursor retrieved from the `pageInfo` of the previous page of results. See the pagination section in your application's API documentation for more info. * `live`: Should this hook re-render when data changes on the backend. See the . * `requestPolicy`: The `urql` request policy to make the request with. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `pause`: Should the hook make a request right now or not. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `suspense`: Should this hook suspend when fetching data. See for more info ##### Returns  `useFindMany` returns two values: a result object with the `data`, `fetching`, and `error` keys for use in your React component's output, and a to trigger a refresh of the hook's data. * `data: GadgetRecordList | null`: The resulting page of records fetched from the backend for your model, once they've arrived * `fetching: boolean`: A boolean describing if the hook is currently making a request to the backend. * `error: Error | null`: An error from the client or server side, if encountered during the request. See the . Without any options, `useFindMany` will fetch the first page of backend records sorted by id. `useFindMany` accepts the `select` option to allow customization of which fields are returned: ```tsx import React from "react"; import { useFindMany } from "@gadgetinc/react"; import { api } from "../api"; export const OnlySomeWidgetFields = () => { // fetch only the widget id and name fields const [{ data, fetching, error }, _refetch] = useFindMany(api.widget, { select: { id: true, name: true, }, }); if (fetching) return "Loading..."; if (error) return `Error loading data: ${error}`; return ( ); }; ``` `useFindMany` accepts a `filter` option to limit which records are returned from the backend. For example, we can filter to return only widgets created since the start of 2022: ```tsx // fetch only the widgets created recently const [{ data, fetching, error }, _refetch] = useFindMany(api.widget, { filter: { createdAt: { greaterThan: new Date(2022, 1, 1) }, }, }); ``` See [your app's API reference](https://docs.gadget-canary.xyz/api/example-app/development/) for more information on which filters are available on what models. `useFindMany` accepts a `sort` option to change the order of the records that are returned. For example, we can sort returned widgets by the `createdAt` field: ```tsx // return the most recently created widgets first const [{ data, fetching, error }, _refetch] = useFindMany(api.widget, { sort: { createdAt: "Descending", }, }); ``` `useFindMany` accepts a `search` option to limit the fetched records to only those matching a given search query. For example, we can search all the backend widgets for those matching the string "penny" in any searchable field: ```tsx // return widgets with "penny" in any searchable field const [{ data, fetching, error }, _refetch] = useFindMany(api.widget, { search: "penny", }); ``` See [your app's API reference](https://docs.gadget-canary.xyz/api/example-app/development/) for more information on the search query syntax and which fields are searchable. `useFindMany` accepts a option to subscribe to changes in the backend data returned, which will trigger re-renders of your react components as that data changes. For example, we can show an up-to-date view of the first page of backend widgets: ```tsx // will update when new widgets are created or on-screen widgets are updated const [{ data, fetching, error }, _refetch] = useFindMany(api.widget, { live: true, }); ``` `useFindMany` accepts pagination arguments for getting the second, third, etc page of results from the backend beyond just the first page. Gadget applications use [Relay Cursor style GraphQL pagination](https://graphql.org/learn/pagination/#pagination-and-edges), where a second page is fetched by asking for the next x many results after a cursor returned with the first page. ```tsx // return the first 10 results after some cursor from somewhere else const [{ data, fetching, error }, _refetch] = useFindMany(api.widget, { first: 10, after: "some-cursor-value", }); // data is a GadgetRecordList object, which has extra properties for inquiring about the pagination state // the current page's start and end cursor are available for use to then make later requests for different pages const { // string used for forward pagination, pass to the `after:` variable endCursor, // string used for backwards pagination, pass to the `before:` variable startCursor, // `data` also reports if there are more pages for fetching // boolean indicating if there is another page to fetch after the `endCursor` hasNextPage, // boolean indicating if there is another page to fetch before the `startCursor` hasPreviousPage, } = data; ``` An easy way to do pagination is using React state, or for a better user experience, using the URL with whatever router system works for your application. We use React state to demonstrate pagination in this example: ```tsx import React, { useState } from "react"; import { useFindMany } from "@gadgetinc/react"; import { api } from "../api"; export const WidgetPaginator = () => { // store the current cursor in the backend data const [cursor, setCursor] = useState(undefined); // pass the current cursor to the `after:` variable, telling the backend to return data after this cursor const [{ data, fetching, error }, _refetch] = useFindMany(api.widget, { first: 20, after: cursor, }); if (fetching) return "Loading..."; if (error) return `Error loading data: ${error}`; return ( <>
    {data?.map((widget) => (
  • {widget.name}
  • ))}
); }; ``` ### `useFindFirst()`  `useFindFirst(manager: ModelFinder, options: FindManyOptions = {}): [{data, fetching, error}, refetch]` `useFindFirst` fetches the first record from your backend Gadget database that matches the given `filter`, `sort`, and `search`. ```tsx import React from "react"; import { useFindFirst } from "@gadgetinc/react"; import { api } from "../api"; export const MostRecentPublishedPost = (props: { id: string }) => { // request the first record from the backend where publishedAt is set and with the most recent publishedAt value const [{ data, fetching, error }, _refetch] = useFindFirst(api.blogPost, { filter: { publishedAt: { isSet: true } }, sort: { publishedAt: "Descending" }, }); if (fetching) return "Loading..."; if (error) return `Error loading data: ${error}`; return Check out our most recent blog post titled {data?.title}; }; ``` ##### Parameters  * `manager`: The model manager for the model you want to find a page of records for. Required. Example: `api.widget`, or `api.shopifyProduct` * `options`: Options for making the call to the backend. Not required and all keys are optional. * `select`: A list of fields and subfields to select. See the docs. Optional. * `filter`: A list of filters to find a record matching. Optional. See the Model Filtering section in your application's API documentation to see the available filters for your models. * `search`: A search string to find a record matching. Optional. See the Model Searching section in your application's API documentation to see the available search syntax. * `sort`: A sort order to order the backend records by. `useFindFirst` will only return the first record matching the given `search` and `filter`, so `sort` can be used to break ties and select a specific record. Optional. See the [sorting section](https://docs.gadget-canary.xyz/api/example-app/development/sorting-and-filtering#sorting) in your application's API documentation for more info. * `live`: Should this hook re-render when data changes on the backend. See the . * `requestPolicy`: The `urql` request policy to make the request with. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `pause`: Should the hook make a request right now or not. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `suspense`: Should this hook suspend when fetching data. See for more info * `select`: A list of fields and subfields to select. See the docs. Optional. * `filter`: A list of filters to find a record matching. Optional. See the Model Filtering section in your application's API documentation to see the available filters for your models. * `search`: A search string to find a record matching. Optional. See the Model Searching section in your application's API documentation to see the available search syntax. * `sort`: A sort order to order the backend records by. `useFindFirst` will only return the first record matching the given `search` and `filter`, so `sort` can be used to break ties and select a specific record. Optional. See the [sorting section](https://docs.gadget-canary.xyz/api/example-app/development/sorting-and-filtering#sorting) in your application's API documentation for more info. * `live`: Should this hook re-render when data changes on the backend. See the . * `requestPolicy`: The `urql` request policy to make the request with. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `pause`: Should the hook make a request right now or not. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `suspense`: Should this hook suspend when fetching data. See for more info ##### Returns  `useFindFirst` returns two values: a result object with the `data`, `fetching`, and `error` keys for inspecting in your React component's output, and a to trigger a refresh of the hook's data. * `data: GadgetRecord | null`: The record fetched from the backend. Is `null` while the data is being loaded, or if a matching record wasn't found. * `fetching: boolean`: A boolean describing if the hook is currently making a request to the backend. * `error: Error | null`: An error from the client or server side, if encountered during the request. Will contain an error if the first record isn't found. See the . If no record is found matching the conditions, `useFindFirst` will return `{data: null, error: new MissingDataError}`. Without any options, `useFindFirst` will fetch the first matching record and cause your component to rerender as the fetch happens and when the data or error arrives. `useFindFirst` can only select some of the fields from the backend model with : ```tsx // fetch the first upside down widget, and only it's id and name fields const [{ data }] = useFindFirst(api.widget, { filter: { state: { equals: "upsideDown" }, }, select: { id: true, name: true, }, }); ``` `useFindFirst` can subscribe to changes in the returned data from the backend with the option, and re-render when the backend data changes: ```tsx // fetch the first upside down widget, and re-render if it's data changes const [{ data }] = useFindFirst(api.widget, { filter: { state: { equals: "upsideDown" }, }, live: true, }); ``` ### `useFindBy()`  `useFindBy(findFunction: ModelFindFunction, fieldValue: any, options: FindByOptions = {}): [{data, fetching, error}, refetch]` `useFindBy` fetches one record from your backend looked up by a specific field and value. `useFindBy` requires a by-field record finder like `.findBySlug` or `.findByEmail` to exist for your model, which are generated by adding a [Unique Validations](https://docs.gadget-canary.xyz/guides/models/fields#uniqueness-validations) to a field. ```tsx import React from "react"; import { useFindBy } from "@gadgetinc/react"; import { api } from "../api"; // get a slug from the URL or similar, and look up a post record by this slug export const PostBySlug = (props: { slug: string }) => { const [{ data, fetching, error }, _refetch] = useFindBy(api.blogPost.findBySlug, props.slug); if (fetching) return "Loading..."; if (error) return `Error loading data: ${error}`; return ( <>

{data?.title}

{data?.body}

); }; ``` ##### Parameters  * `findFunction`: The model finder function from your application's API client for finding records by a specific field. Gadget generates these finder functions for the fields where they are available. Changes to your Gadget backend schema may be required to get these to exist. Required. Example: `api.widget.findBySlug`, or `api.user.findByEmail`. * `fieldValue`: The value of the field to search for a record using. This is which slug or email you'd pass to `api.widget.findBySlug` or `api.user.findByEmail`. * `options`: Options for making the call to the backend. Not required and all keys are optional. * `select`: A list of fields and subfields to select. See the docs. * `live`: Should this hook re-render when data changes on the backend. See the . * `requestPolicy`: The `urql` request policy to make the request with. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `pause`: Should the hook make a request right now or not. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `suspense`: Should this hook suspend when fetching data. See for more info * `select`: A list of fields and subfields to select. See the docs. * `live`: Should this hook re-render when data changes on the backend. See the . * `requestPolicy`: The `urql` request policy to make the request with. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `pause`: Should the hook make a request right now or not. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `suspense`: Should this hook suspend when fetching data. See for more info ##### Returns  `useFindBy` returns two values: a result object with the `data`, `fetching`, and `error` keys for inspecting in your React component's output, and a to trigger a refresh of the hook's data. * `data: GadgetRecord | null`: The record fetched from the backend. Is `null` while the data is being loaded, or if a matching record wasn't found for the given `fieldValue`. * `fetching: boolean`: A boolean describing if the hook is currently making a request to the backend. * `error: Error | null`: An error from the client or server side, if encountered during the request. Will contain an error if a matching record isn't found. See the . If no record is found matching the conditions, then the returned object will have `null` for the `data`. `useFindBy(api.widget.findByEmail, "hi@gadget.dev")` is the React equivalent of `api.widget.findByEmail("hi@gadget.dev")` Without any options, `useFindBy` will fetch the record with the given field value, and cause your component to rerender as the fetch happens and when the data or error arrives: ```tsx import React from "react"; import { useFindBy } from "@gadgetinc/react"; import { api } from "../api"; // get a slug from the URL or similar, and look up a post record by this slug export const PostBySlug = (props: { slug: string }) => { const [{ data, fetching, error }, _refetch] = useFindBy(api.blogPost.findBySlug, props.slug); if (fetching) return "Loading..."; if (error) return `Error loading data: ${error}`; return ( <>

{data?.title}

{data?.body}

); }; ``` `useFindBy` can take options that allow the customization of which fields are returned: ```tsx // fetch only a post id and title fields const [{ data, fetching, error }, _refetch] = useFindBy(api.blogPost.findBySlug, "some-slug", { select: { id: true, title: true } }); ``` The `refetch` function returned as the second element can be executed in order to trigger a refetch of the most up to date data from the backend. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/basics/react-preact/#reexecuting-queries) on re-executing queries for more information. ### `useMaybeFindFirst()`  `useMaybeFindFirst(manager: ModelFinder, options: FindManyOptions = {}): [{data, fetching, error}, refetch]` `useMaybeFindFirst` fetches the first record from your backend Gadget database that matches the given `filter`, `sort`, and `search` parameters. ```tsx import React from "react"; import { useMaybeFindFirst } from "@gadgetinc/react"; import { api } from "../api"; export const MostRecentPublishedPost = (props: { id: string }) => { // request the first record from the backend where publishedAt is set and with the most recent publishedAt value const [{ data, fetching, error }, _refetch] = useMaybeFindFirst(api.blogPost, { filter: { publishedAt: { isSet: true } }, sort: { publishedAt: "Descending" }, }); if (fetching) return "Loading..."; if (error) return `Error loading data: ${error}`; if (data) { return Check out our most recent blog post titled {data.title}; } else { // no first record found return null; } }; ``` `useMaybeFindFirst` returns `data: null` if no record is found in the backend database, and otherwise works identically to `useFindFirst`. See for more details on the options `useMaybeFindFirst` accepts. ### `useAction()`  `useAction(actionFunction: ModelActionFunction, options: UseActionOptions = {}): [{data, fetching, error}, refetch]` ```tsx import React from "react"; import { useAction } from "@gadgetinc/react"; import { api } from "../api"; export const CreatePost = () => { const [{ data, fetching, error }, createBlogPost] = useAction(api.blogPost.create); return ( ); }; ``` `useAction` is a hook for running a backend action on one record of a Gadget model. `useAction` must be passed an action function from an instance of your application's generated API client. Options: ##### Parameters  * `actionFunction`: The model action function from your application's API client for acting on records. Gadget generates these action functions for each action defined on backend Gadget models. Required. Example: `api.widget.create`, or `api.user.update` or `api.blogPost.publish`. * `options`: Options for making the call to the backend. Not required and all keys are optional. * `select`: A list of fields and subfields to select. See the docs. * `requestPolicy`: The `urql` request policy to make the request with. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `pause`: Should the hook make a request right now or not. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `suspense`: Should this hook suspend when fetching data. See for more info * `select`: A list of fields and subfields to select. See the docs. * `requestPolicy`: The `urql` request policy to make the request with. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `pause`: Should the hook make a request right now or not. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `suspense`: Should this hook suspend when fetching data. See for more info ##### Returns  `useAction` returns two values: a result object with the `data`, `fetching`, and `error` keys for inspecting in your React component's output, and a `act` function to actually run the backend action. `useAction` is a rule-following React hook that wraps action execution, which means it doesn't just run the action as soon as the hook is invoked. Instead, `useAction` returns a configured function that will actually run the action, which you need to call in response to some user event. The `act` function accepts the action inputs as arguments -- not `useAction` itself. `useAction`'s result will return the `data`, `fetching`, and `error` details for the most recent execution of the action. * `data: GadgetRecord | null`: The record fetched from the backend after a mutation. Is `null` while before the mutation is run and while it is currently ongoing. * `fetching: boolean`: A boolean describing if the hook is currently making a request to the backend. * `error: Error | null`: An error from the client or server side, if encountered during the mutation. Will contain an error if the client passed invalid data, if the server failed to complete the action, or if a network error was encountered. See the . For example, we can create a button that creates a post when clicked, and then shows the post once it has been created: ```tsx import React from "react"; import { useAction } from "@gadgetinc/react"; import { api } from "../api"; export const CreatePost = () => { const [{ data, fetching, error }, createBlogPost] = useAction(api.blogPost.create); // deal with all the states of the result object if (fetching) return "Loading..."; if (error) return `Error loading data: ${error}`; if (!data) { return ( ); } else { return ( <>

{data.title}

{data.body}

); } }; ``` We can also run actions on existing models by passing the `id:` in with the action parameters: ```tsx import React, { useState } from "react"; import { useAction } from "@gadgetinc/react"; import { api } from "../api"; export const UpdatePost = (props: { id: string }) => { // invoke the `useAction` hook, getting back a result object and an action runner function every render const [{ data, fetching, error }, updateBlogPost] = useAction(api.blogPost.update); const [title, setTitle] = useState(""); if (fetching) return "Loading..."; if (error) return `Error loading data: ${error}`; return (
{ // pass the id of the blog post we're updating as one parameter, and the new post attributes as another updateBlogPost({ id: props.id, title, }); }} > setTitle(event.target.value)} /> Submit
); }; ``` `useAction` can take options that allow the customization of which fields are returned on the acted-upon record: ```tsx // fetch only a post id and title fields const [{ data, fetching, error }, updateBlogPost] = useAction(api.blogPost.update, { select: { id: true, title: true } }); ``` ### `useGlobalAction()`  `useGlobalAction(actionFunction: GlobalActionFunction, options: UseGlobalActionOptions = {}): [{data, fetching, error}, refetch]` `useGlobalAction` is a hook for running a backend Global Action. ```tsx import React from "react"; import { useGlobalAction } from "@gadgetinc/react"; import { api } from "../api"; export const PurgeData = () => { const [{ data, fetching, error }, runPurge] = useGlobalAction(api.purgeData); return ( ); }; ``` ##### Parameters  * `globalActionFunction`: The action function from your application's API client. Gadget generates these global action functions for each global action defined in your Gadget backend. Required. Example: `api.runSync`, or `api.purgeData` (corresponding to Global Actions named `Run Sync` or `Purge Data`). * `options`: Options for making the call to the backend. Not required and all keys are optional. * `requestPolicy`: The `urql` request policy to make the request with. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `pause`: Should the hook make a request right now or not. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `requestPolicy`: The `urql` request policy to make the request with. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `pause`: Should the hook make a request right now or not. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) ##### Returns  `useGlobalAction` returns two values: a result object with the `data`, `fetching`, and `error` keys for inspecting in your React component's output, and an `act` function to actually run the backend global action. `useGlobalAction` is a rule-following React hook that wraps action execution, which means it doesn't just run the action as soon as the hook is invoked. Instead, `useGlobalAction` returns a configured function which you need to call in response to some event. This `act` function accepts the action inputs as arguments. `useGlobalAction`'s result will return the `data`, `fetching`, and `error` details for the most recent execution of the action. * `data: Record<string, any> | null`: The data returned by the global action from the backend. Is `null` while before the mutation is run and while it is currently ongoing. * `fetching: boolean`: A boolean describing if the hook is currently making a request to the backend. * `error: Error | null`: An error from the client or server side, if encountered during the mutation. Will contain an error if the client passed invalid data, if server failed to complete the mutation, or if a network error was encountered. See the . For example, we can create a button that runs a Global Action called `purgeData` when clicked, and shows the result after it has been run: ```tsx import React from "react"; import { useGlobalAction } from "@gadgetinc/react"; import { api } from "../api"; export const PurgeData = () => { const [{ data, fetching, error }, runPurge] = useGlobalAction(api.purgeData); // deal with all the states of the result object if (fetching) return "Loading..."; if (error) return `Error running global action: ${error}`; if (!data) { return ( ); } else { return "Purge completed"; } }; ``` ### `useView()`  `useView(view: F | string, variablesOrOptions?: VariablesT | Omit, options: Omit): [{data, fetching, error}, refetch]` `useView` is a hook for calling both named and inline computed views. ```tsx export function Leaderboard() { const [result, _refetch] = useView(api.leaderboard); if (result.error) return <>Error: {result.error.toString()}; if (result.fetching && !result.data) return <>Fetching...; if (!result.data) return <>No data found; return ( <> {result.data.players.map((player) => (
{player.name}: {player.totalScore}
))} ); } ``` ##### Parameters  * `view`: The view function from your application's API client or a stringified Gelly snippet. Gadget generates view functions for each named computed view defined in your Gadget backend. Required. * `variables`: Variables to pass to the computed view. Only required if the view expects variables. * `options`: Options for making the call to the backend. Not required and all keys are optional. * `select`: A list of fields and subfields to select. See the docs. * `requestPolicy`: The `urql` request policy to make the request with. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `pause`: Should the hook make a request right now or not. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `suspense`: Should this hook suspend when fetching data. See for more info * `select`: A list of fields and subfields to select. See the docs. * `requestPolicy`: The `urql` request policy to make the request with. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `pause`: Should the hook make a request right now or not. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `suspense`: Should this hook suspend when fetching data. See for more info ##### Returns  `useView` returns two values: a result object with the `data`, `fetching`, and `error` keys for inspecting in your React component's output, and a to trigger a refresh of the hook's data. * `data: ViewResult | null`: The result of the computed view query fetched from the backend. Is `null` while the data is being loaded, or if the record wasn't found. * `fetching: boolean`: A boolean describing if the hook is currently requesting data from the backend. * `error: Error | null`: An error from the client or server side, if encountered during the request. Will contain an error if the record isn't found by `id`. See the . Inline views can be run with `useView` by passing in a Gelly query as a string: ```tsx export function Leaderboard() { const [result, _refetch] = useView(`{ players { name totalScore: sum(results.score) [order by sum(results.score) desc limit 5] } }`); if (result.error) return <>Error: {result.error.toString()}; if (result.fetching && !result.data) return <>Fetching...; if (!result.data) return <>No data found; return ( <> {result.data.players.map((player) => (
{player.name}: {player.score}
))} ); } ``` Computed view parameters can also be passed into both named and inline computed views executed using the `useView` hook: ```tsx export function Leaderboard() { // will only return the top 2 players const [{ data, fetching, error }] = useView( `($numTopPlayers: Integer) { players { name totalScore: sum(results.score) [order by sum(results.score) desc limit $numTopPlayers] } }`, { numTopPlayers: 2 } ); if (result.error) return <>Error: {result.error.toString()}; if (result.fetching && !result.data) return <>Fetching...; if (!result.data) return <>No data found; return ( <> {result.data.players.map((player) => (
{player.name}: {player.totalScore}
))} ); } ``` You should always pass dynamic values to inline computed views using the `variables` parameter. It allows for better escaping of values with spaces or special characters, and improved performance under the hood. ### `useGet()`  `useGet(singletonModelManager: SingletonModelManager, options: GetOptions = {}): [{data, fetching, error}, refetch]` `useGet` fetches a singleton record for an `api.currentSomething` style model manager. `useGet` fetches one global record, which is most often the current session. If you'd like to access the current session on the frontend, use the hook ```tsx import React from "react"; import { useGet } from "@gadgetinc/react"; import { api } from "../api"; export const CurrentSessionId = () => { const [{ data, fetching, error }, _refetch] = useGet(api.currentSession); if (fetching) return "Loading..."; if (error) return `Error loading data: ${error}`; return Current session: {data?.id}; }; ``` ##### Parameters  * `singletonModelManager`: The singleton model manager available on the generated API client for your application. The passed model manager _must_ be one of the `currentSomething` model managers. `useGet` can't be used with other model managers that don't have a `.get` function. Example: `api.currentSession`. * `options`: Options for making the call to the backend. Not required and all keys are optional. * `select`: A list of fields and subfields to select. See the docs. * `requestPolicy`: The `urql` request policy to make the request with. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `pause`: Should the hook make a request right now or not. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `suspense`: Should this hook suspend when fetching data. See for more info * `select`: A list of fields and subfields to select. See the docs. * `requestPolicy`: The `urql` request policy to make the request with. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `pause`: Should the hook make a request right now or not. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/api/urql/) * `suspense`: Should this hook suspend when fetching data. See for more info ##### Returns  `useGet` returns two values: a result object with the `data`, `fetching`, and `error` keys for inspecting in your React component's output, and a to trigger a refresh of the hook's data. * `data: GadgetRecord | null`: The record fetched from the backend. Is `null` while the data is being loaded, or if the record wasn't found. * `fetching: boolean`: A boolean describing if the hook is currently making a request to the backend. * `error: Error | null`: An error from the client or server side, if encountered during the request. Will contain an error if the singleton record isn't found. See the . `useGet(api.currentSession)` retrieves the current global session for the current browser ```tsx import React from "react"; import { useGet } from "@gadgetinc/react"; import { api } from "../api"; export const CurrentSessionId = () => { const [{ data, fetching, error }, _refetch] = useGet(api.currentSession); if (fetching) return "Loading..."; if (error) return `Error loading data: ${error}`; return Current session: {data?.id}; }; ``` `useGet` can take options which allow the customization of which fields are returned on the selected record: ```tsx // fetch only the session id and state fields const [{ data, fetching, error }, _refetch] = useGet(api.currentSession, { select: { id: true, createdAt: true, }, }); ``` The `refetch` function returned as the second element can be executed to trigger a refetch of the most up-to-date data from the backend. See [`urql`'s docs](https://formidable.com/open-source/urql/docs/basics/react-preact/#reexecuting-queries) on re-executing queries for more information. ### `useEnqueue()`  `useEnqueue(actionFunction: ModelActionFunction || GlobalActionFunction, actionInput, backgroundActionOptions = {}): [{error, fetching, handle}, enqueue]` `useEnqueue` facilitates the enqueuing of actions to be executed in the background. ```tsx import React from "react"; import { useEnqueue } from "@gadgetinc/react"; import { api } from "../api"; export function CreateUserButton(props: { name: string; email: string }) { const [{ error, fetching, handle }, enqueue] = useEnqueue(api.user.create); const onClick = () => enqueue( { // actionInput name: props.name, email: props.email, }, { // backgroundActionoptions id: `send-email-action-${props.email}`, } ); return ( <> {error && <>Failed to enqueue user create: {error.toString()}} {fetching && <>Enqueuing action...} {handle && <>Enqueued action with background action id={handle.id}} ); } ``` ##### Parameters  * `actionFunction: ModelActionFunction || GlobalActionFunction`: Either a model or global action. * `actionInput: Input | null`: The parameters or data passed to a model or global action. * `backgroundActionOptions (optional)`: The options for how the background action runs, for more information check out the [reference here](https://docs.gadget-canary.xyz/reference/gadget-server#enqueue-action-input-options). ##### Returns  `useEnqueue` doesn't submit the background action when invoked but instead returns a function for enqueuing the action in response to the event. ### `useActionForm()`  `useActionForm(actionFunction: ModelActionFunction, options: UseActionFormOptions = {}): UseActionFormResult` `useActionForm` manages form state for calling actions in your Gadget backend. `useActionForm` can fetch the record for editing, manage the state of the fields as the user changes them in a form, track validations and errors, and then call the action with the form data when the user submits the form. `useActionForm` can call Actions on models as well as Global Actions. `useActionForm` wraps the excellent [`react-hook-form` library](https://react-hook-form.com/) and provides all the same state management primitives that `react-hook-form` does, in addition to Gadget-specific goodies like automatic record fetching, automatic action calling and type safety. ```tsx import React from "react"; import { useActionForm } from "@gadgetinc/react"; import { api } from "../api"; const PostForm = () => { const { register, submit } = useActionForm(api.post.create); return (