Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
at main 448 lines 16 kB view raw view rendered
1--- 2title: React/Preact Bindings 3order: 0 4--- 5 6# React/Preact 7 8This guide covers how to install and setup `urql` and the `Client`, as well as query and mutate data, 9with React and Preact. Since the `urql` and `@urql/preact` packages share most of their API and are 10used in the same way, when reading the documentation on React, all examples are essentially the same, 11except that we'd want to use the `@urql/preact` package instead of the `urql` package. 12 13## Getting started 14 15### Installation 16 17Installing `urql` is as quick as you'd expect, and you won't need any other packages to get started 18with at first. We'll install the package with our package manager of choice. 19 20```sh 21yarn add urql 22# or 23npm install --save urql 24``` 25 26To use `urql` with Preact, we have to install `@urql/preact` instead of `urql` and import from 27that package instead. Otherwise all examples for Preact will be the same. 28 29Most libraries related to GraphQL also need the `graphql` package to be installed as a peer 30dependency, so that they can adapt to your specific versioning requirements. That's why we'll need 31to install `graphql` alongside `urql`. 32 33Both the `urql` and `graphql` packages follow [semantic versioning](https://semver.org) and all 34`urql` packages will define a range of compatible versions of `graphql`. Watch out for breaking 35changes in the future however, in which case your package manager may warn you about `graphql` being 36out of the defined peer dependency range. 37 38### Setting up the `Client` 39 40The `urql` and `@urql/preact` packages export a `Client` class, which we can use to 41create the GraphQL client. This central `Client` manages all of our GraphQL requests and results. 42 43```js 44import { Client, cacheExchange, fetchExchange } from 'urql'; 45 46const client = new Client({ 47 url: 'http://localhost:3000/graphql', 48 exchanges: [cacheExchange, fetchExchange], 49}); 50``` 51 52At the bare minimum we'll need to pass an API's `url` and `exchanges` when we create a `Client` 53to get started. 54 55Another common option is `fetchOptions`. This option allows us to customize the options that will be 56passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object, or 57a function returning an options object. 58 59In the following example we'll add a token to each `fetch` request that our `Client` sends to our 60GraphQL API. 61 62```js 63const client = new Client({ 64 url: 'http://localhost:3000/graphql', 65 exchanges: [cacheExchange, fetchExchange], 66 fetchOptions: () => { 67 const token = getToken(); 68 return { 69 headers: { authorization: token ? `Bearer ${token}` : '' }, 70 }; 71 }, 72}); 73``` 74 75### Providing the `Client` 76 77To make use of the `Client` in React & Preact we will have to provide it via the 78[Context API](https://reactjs.org/docs/context.html). This may be done with the help of 79the `Provider` export. 80 81```jsx 82import { Client, Provider, cacheExchange, fetchExchange } from 'urql'; 83 84const client = new Client({ 85 url: 'http://localhost:3000/graphql', 86 exchanges: [cacheExchange, fetchExchange], 87}); 88 89const App = () => ( 90 <Provider value={client}> 91 <YourRoutes /> 92 </Provider> 93); 94``` 95 96Now every component and element inside and under the `Provider` can use GraphQL queries that 97will be sent to our API. 98 99## Queries 100 101Both libraries offer a `useQuery` hook and a `Query` component. The latter accepts the same 102parameters, but we won't cover it in this guide. [Look it up in the API docs if you prefer 103render-props components.](../api/urql.md#query-component) 104 105### Run a first query 106 107For the following examples, we'll imagine that we're querying data from a GraphQL API that contains 108todo items. Let's dive right into it! 109 110```jsx 111import { gql, useQuery } from 'urql'; 112 113const TodosQuery = gql` 114 query { 115 todos { 116 id 117 title 118 } 119 } 120`; 121 122const Todos = () => { 123 const [result, reexecuteQuery] = useQuery({ 124 query: TodosQuery, 125 }); 126 127 const { data, fetching, error } = result; 128 129 if (fetching) return <p>Loading...</p>; 130 if (error) return <p>Oh no... {error.message}</p>; 131 132 return ( 133 <ul> 134 {data.todos.map(todo => ( 135 <li key={todo.id}>{todo.title}</li> 136 ))} 137 </ul> 138 ); 139}; 140``` 141 142Here we have implemented our first GraphQL query to fetch todos. We see that `useQuery` accepts 143options and returns a tuple. In this case we've set the `query` option to our GraphQL query. The 144tuple we then get in return is an array that contains a result object, and a re-execute function. 145 146The result object contains several properties. The `fetching` field indicates whether the hook is 147loading data, `data` contains the actual `data` from the API's result, and `error` is set when either 148the request to the API has failed or when our API result contained some `GraphQLError`s, which 149we'll get into later on the ["Errors" page](./errors.md). 150 151### Variables 152 153Typically we'll also need to pass variables to our queries, for instance, if we are dealing with 154pagination. For this purpose the `useQuery` hook also accepts a `variables` option, which we can use 155to supply variables to our query. 156 157```jsx 158const TodosListQuery = gql` 159 query ($from: Int!, $limit: Int!) { 160 todos(from: $from, limit: $limit) { 161 id 162 title 163 } 164 } 165`; 166 167const Todos = ({ from, limit }) => { 168 const [result, reexecuteQuery] = useQuery({ 169 query: TodosListQuery, 170 variables: { from, limit }, 171 }); 172 173 // ... 174}; 175``` 176 177As when we're sending GraphQL queries manually using `fetch`, the variables will be attached to the 178`POST` request body that is sent to our GraphQL API. 179 180Whenever the `variables` (or the `query`) option on the `useQuery` hook changes `fetching` will 181switch to `true`, and a new request will be sent to our API, unless a result has already been cached 182previously. 183 184### Pausing `useQuery` 185 186In some cases we may want `useQuery` to execute a query when a pre-condition has been met, and not 187execute the query otherwise. For instance, we may be building a form and want a validation query to 188only take place when a field has been filled out. 189 190Since hooks in React can't just be commented out, the `useQuery` hook accepts a `pause` option that 191temporarily _freezes_ all changes and stops requests. 192 193In the previous example we've defined a query with mandatory arguments. The `$from` and `$limit` 194variables have been defined to be non-nullable `Int!` values. 195 196Let's pause the query we've just 197written to not execute when these variables are empty, to prevent `null` variables from being 198executed. We can do this by setting the `pause` option to `true`: 199 200```jsx 201const Todos = ({ from, limit }) => { 202 const shouldPause = from === undefined || from === null || limit === undefined || limit === null; 203 const [result, reexecuteQuery] = useQuery({ 204 query: TodosListQuery, 205 variables: { from, limit }, 206 pause: shouldPause, 207 }); 208 209 // ... 210}; 211``` 212 213Now whenever the mandatory `$from` or `$limit` variables aren't supplied the query won't be executed. 214This also means that `result.data` won't change, which means we'll still have access to our old data 215even though the variables may have changed. 216 217### Request Policies 218 219As has become clear in the previous sections of this page, the `useQuery` hook accepts more options 220than just `query` and `variables`. Another option we should touch on is `requestPolicy`. 221 222The `requestPolicy` option determines how results are retrieved from our `Client`'s cache. By 223default, this is set to `cache-first`, which means that we prefer to get results from our cache, but 224are falling back to sending an API request. 225 226Request policies aren't specific to `urql`'s React API, but are a common feature in its core. [You 227can learn more about how the cache behaves given the four different policies on the "Document 228Caching" page.](../basics/document-caching.md) 229 230```jsx 231const [result, reexecuteQuery] = useQuery({ 232 query: TodosListQuery, 233 variables: { from, limit }, 234 requestPolicy: 'cache-and-network', 235}); 236``` 237 238Specifically, a new request policy may be passed directly to the `useQuery` hook as an option. 239This policy is then used for this specific query. In this case, `cache-and-network` is used and 240the query will be refreshed from our API even after our cache has given us a cached result. 241 242Internally, the `requestPolicy` is just one of several "**context** options". The `context` 243provides metadata apart from the usual `query` and `variables` we may pass. This means that 244we may also change the `Client`'s default `requestPolicy` by passing it there. 245 246```js 247import { Client, cacheExchange, fetchExchange } from 'urql'; 248 249const client = new Client({ 250 url: 'http://localhost:3000/graphql', 251 exchanges: [cacheExchange, fetchExchange], 252 // every operation will by default use cache-and-network rather 253 // than cache-first now: 254 requestPolicy: 'cache-and-network', 255}); 256``` 257 258### Context Options 259 260As mentioned, the `requestPolicy` option on `useQuery` is a part of `urql`'s context options. 261In fact, there are several more built-in context options, and the `requestPolicy` option is 262one of them. Another option we've already seen is the `url` option, which determines our 263API's URL. These options aren't limited to the `Client` and may also be passed per query. 264 265```jsx 266import { useMemo } from 'react'; 267import { useQuery } from 'urql'; 268 269const Todos = ({ from, limit }) => { 270 const [result, reexecuteQuery] = useQuery({ 271 query: TodosListQuery, 272 variables: { from, limit }, 273 context: useMemo( 274 () => ({ 275 requestPolicy: 'cache-and-network', 276 url: 'http://localhost:3000/graphql?debug=true', 277 }), 278 [] 279 ), 280 }); 281 282 // ... 283}; 284``` 285 286As we can see, the `context` property for `useQuery` accepts any known `context` option and can be 287used to alter them per query rather than globally. The `Client` accepts a subset of `context` 288options, while the `useQuery` option does the same for a single query. 289[You can find a list of all `Context` options in the API docs.](../api/core.md#operationcontext) 290 291### Reexecuting Queries 292 293The `useQuery` hook updates and executes queries whenever its inputs, like the `query` or 294`variables` change, but in some cases we may find that we need to programmatically trigger a new 295query. This is the purpose of the `reexecuteQuery` function, which is the second item in the tuple 296that `useQuery` returns. 297 298Triggering a query programmatically may be useful in a couple of cases. It can for instance be used 299to refresh the hook's data. In these cases we may also override the `requestPolicy` of our query just 300once and set it to `network-only` to skip the cache. 301 302```jsx 303const Todos = ({ from, limit }) => { 304 const [result, reexecuteQuery] = useQuery({ 305 query: TodosListQuery, 306 variables: { from, limit }, 307 }); 308 309 const refresh = () => { 310 // Refetch the query and skip the cache 311 reexecuteQuery({ requestPolicy: 'network-only' }); 312 }; 313}; 314``` 315 316Calling `refresh` in the above example will execute the query again forcefully, and will skip the 317cache, since we're passing `requestPolicy: 'network-only'`. 318 319Furthermore the `reexecuteQuery` function can also be used to programmatically start a query even 320when `pause` is set to `true`, which would usually stop all automatic queries. This can be used to 321perform one-off actions, or to set up polling. 322 323```jsx 324import { useEffect } from 'react'; 325import { useQuery } from 'urql'; 326 327const Todos = ({ from, limit }) => { 328 const [result, reexecuteQuery] = useQuery({ 329 query: TodosListQuery, 330 variables: { from, limit }, 331 pause: true, 332 }); 333 334 useEffect(() => { 335 if (result.fetching) return; 336 337 // Set up to refetch in one second, if the query is idle 338 const timerId = setTimeout(() => { 339 reexecuteQuery({ requestPolicy: 'network-only' }); 340 }, 1000); 341 342 return () => clearTimeout(timerId); 343 }, [result.fetching, reexecuteQuery]); 344 345 // ... 346}; 347``` 348 349There are some more tricks we can use with `useQuery`. [Read more about its API in the API docs for 350it.](../api/urql.md#usequery) 351 352## Mutations 353 354Both libraries offer a `useMutation` hook and a `Mutation` component. The latter accepts the same 355parameters, but we won't cover it in this guide. [Look it up in the API docs if you prefer 356render-props components.](../api/urql.md#mutation-component) 357 358### Sending a mutation 359 360Let's again pick up an example with an imaginary GraphQL API for todo items, and dive into an 361example! We'll set up a mutation that _updates_ a todo item's title. 362 363```jsx 364const UpdateTodo = ` 365 mutation ($id: ID!, $title: String!) { 366 updateTodo (id: $id, title: $title) { 367 id 368 title 369 } 370 } 371`; 372 373const Todo = ({ id, title }) => { 374 const [updateTodoResult, updateTodo] = useMutation(UpdateTodo); 375}; 376``` 377 378Similar to the `useQuery` output, `useMutation` returns a tuple. The first item in the tuple again 379contains `fetching`, `error`, and `data` — it's identical since this is a common pattern of how 380`urql` presents [operation results](../api/core.md#operationresult). 381 382Unlike the `useQuery` hook, the `useMutation` hook doesn't execute automatically. At this point in 383our example, no mutation will be performed. To execute our mutation we instead have to call the 384execute function — `updateTodo` in our example — which is the second item in the tuple. 385 386### Using the mutation result 387 388When calling our `updateTodo` function we have two ways of getting to the result as it comes back 389from our API. We can either use the first value of the returned tuple, our `updateTodoResult`, or 390we can use the promise that `updateTodo` returns. 391 392```jsx 393const Todo = ({ id, title }) => { 394 const [updateTodoResult, updateTodo] = useMutation(UpdateTodo); 395 396 const submit = newTitle => { 397 const variables = { id, title: newTitle || '' }; 398 updateTodo(variables).then(result => { 399 // The result is almost identical to `updateTodoResult` with the exception 400 // of `result.fetching` not being set. 401 // It is an OperationResult. 402 }); 403 }; 404}; 405``` 406 407The result is useful when your UI has to display progress on the mutation, and the returned 408promise is particularly useful when you're adding side effects that run after the mutation has 409completed. 410 411### Handling mutation errors 412 413It's worth noting that the promise we receive when calling the execute function will never 414reject. Instead it will always return a promise that resolves to a result. 415 416If you're checking for errors, you should use `result.error` instead, which will be set 417to a `CombinedError` when any kind of errors occurred while executing your mutation. 418[Read more about errors on our "Errors" page.](./errors.md) 419 420```jsx 421const Todo = ({ id, title }) => { 422 const [updateTodoResult, updateTodo] = useMutation(UpdateTodo); 423 424 const submit = newTitle => { 425 const variables = { id, title: newTitle || '' }; 426 updateTodo(variables).then(result => { 427 if (result.error) { 428 console.error('Oh no!', result.error); 429 } 430 }); 431 }; 432}; 433``` 434 435There are some more tricks we can use with `useMutation`.<br /> 436[Read more about its API in the API docs for it.](../api/urql.md#usemutation) 437 438## Reading on 439 440This concludes the introduction for using `urql` with React or Preact. The rest of the documentation 441is mostly framework-agnostic and will apply to either `urql` in general, or the `@urql/core` package, 442which is the same between all framework bindings. Hence, next we may want to learn more about one of 443the following to learn more about the internals: 444 445- [How does the default "document cache" work?](./document-caching.md) 446- [How are errors handled and represented?](./errors.md) 447- [A quick overview of `urql`'s architecture and structure.](../architecture.md) 448- [Setting up other features, like authentication, uploads, or persisted queries.](../advanced/README.md)