Create a Custom Exchange for urql to Filter Out Mutations when Offline

Kadi Kraman
InstructorKadi Kraman

Share this video with your friends

Send Tweet
Published 2 years ago
Updated 2 years ago

We'll create an exchange that filters out mutations that are executed while the user is offline and map them to an error result. When the user tries to add or remove a bookmark when offline, show an informative alert message.

Resources: Authoring exchanges: https://formidable.com/open-source/urql/docs/advanced/authoring-exchanges/ Wonka: https://github.com/kitten/wonka

Checkpoint: Add an offline mutation exchange

Instructor: [0:00] We have an app that is Read Only when the user is offline. However, if the user was to add a bookmark or try to remove a bookmark, the action would fail silently.

[0:09] We'd have no easy way to distinguish between request failed because the user was offline, or request failed due to an API error. Urql is an exchange-based library. Right now, we're using the Dedup exchange, Offline exchange, and the Fetch exchange.

[0:22] This architectural decision makes Urql incredibly flexible and allows us to create our own exchanges to filter, modify, or change any of the requests or responses as we see fit. The exchanges in Urql are stream-based, and are using the stream library, Wonka.

[0:36] Let's create a const offline mutation exchange. This will be a function returning an exchange. We'll need to import exchange from Urql. Let's do an Arrow function, and now let's return. Let's destructure forward. This will be another Arrow function.

[0:50] We'll return the operation stream. Let's just do, return forward operations. This is what we call a no-op exchange, meaning that it takes the operations and simply forwards them on without modifying them. Let's import NetInfo from React Native NetInfo. Just above the exchange, let's declare a letConnected. Let's initialize it to true.

[1:09] Let's do NetInfo addEventListener. Here we'll get access to the new value, or the network state. We'll update our connected variable to be true if state.isConnected is true. Adding an event listener without cleaning it up is a likely cause of memory leaks.

[1:24] Let's declare a letDisconnect outside the mutation exchange. This addEventListener will return a Cleaner function. Let's assign it to disconnect, so disconnect = NetInfo addEventListener. Before we create the event listener, let's check.

[1:38] If disconnect exists, then call disconnect and assign it to undefined. This ensures that the event listener gets cleaned up if the exchange is regenerated. Let's import share and pipe from Wonka.

[1:50] Wonka is a peer dependency of Urql, meaning that you'll already have Wonka installed if you've installed Urql. In our mutation exchange, let's do const shared = pipe. We'll pass in the operations and share.

[2:02] We'll want to split this operations into two groups, mutations when the user is offline and everything else. Let's also import filter from Wonka. We'll do const offline mutations. We'll pipe the shared operations.

[2:15] We'll filter out the operations where the operation not-kind is a mutation, only if we are not connected. For everything else, we'll do const rest, and we'll pipe the shared operations again, and we'll filter out the converse.

[2:29] We'll keep operations where operation not-kind is not mutation, or where operation not-kind is a mutation, but we are connected. We're taking all the operations that are coming in to exchange and we're splitting them into two groups, mutations that were executed while the device is offline and everything else. Let's import make error result from Urql. Let's also import map from Wonka. In our offline mutations, let's map all these operations into an error result.

[2:58] We'll pass in the operation and a new error with an error message of, "You are Offline." Finally, we'll want to import merge from Wonka. Here, where we previously returned the operations, let's replace this with a merge and this will take an array.

[3:12] We'll still want to forward everything else on to the next exchange. We'll want to send back the offline mutations. When we go to the exchange array, let's add the offline mutation exchange just before the fetch exchange. In our story.tsx, let's create a const handle addBookmark.

[3:27] This will be a React useCallback with an arrow function. Let's have it call addBookmark and update the unpress. If we make this an asynchronous function and do const result = await addBookmark, and console log results.error, and update dependencies array.

[3:44] Make sure you are both offline and you don't have the API running, and reload the app. When we tap on addBookmark, we will see a network error saying you are offline, which we mapped all the offline mutations to in our offline mutation exchange.

[3:58] Let's import alert from React Native. Let's do, if results.error and result.error.message includes you are offline, then let it show an alert and let's say, you are offline. For the message, "Please connect to the Internet to add the storage, your bookmarks." Let's reload.

[4:15] When we try to add this to our bookmarks, we will get an alert telling us exactly why we can't. Let's do the same with handle removeBookmark. This will be React, useCallback. Let's copy the removeBookmark mutation and we'll convert this to an async function.

[4:30] This will do const result = await addBookmark. We'll update the on-press to use the callback instead. In here, we can add the same if-statement only saying, "Please connect to the Internet to remove the story from your bookmarks," and update dependencies array.

[4:47] After we reload, we can go to the bookmarks tab and we're trying to remove a bookmark. We will also get an informative message.