DEV Community

Cover image for How to revalidate data on focus with SWR and React Native
Ana Beatriz
Ana Beatriz

Posted on • Edited on

How to revalidate data on focus with SWR and React Native

"With SWR, components will get a stream of data updates constantly and automatically. And the UI will be always fast and reactive." - SWR Documentation

But some main features like revalidateOnFocus or revalidateOnReconnect are not supported by default when developing with React Native, and this issue was already reported.

From this issue, the package swr-react-native was created to help us!

In this tutorial I will show how to revalidate data when a screen is focused again.

Step 1: Create the project
After following the official React Native tutorial to set up the development environment, you can run the command below to create your project:

npx react-native init swr-rn-revalidateOnFocus
                    # or whatever name you like
Enter fullscreen mode Exit fullscreen mode

Step 2: Install dependencies
After that, we can add the dependencies that we need:
React navigation to handle navigation between screens

yarn add \
@react-navigation/bottom-tabs \
@react-navigation/native \
@react-navigation/stack
Enter fullscreen mode Exit fullscreen mode

react-navigation needs some libraries included in our project:

yarn add \
react-native-gesture-handler \
react-native-safe-area-context \
react-native-screens
Enter fullscreen mode Exit fullscreen mode

Next, let's install swr and swr-react-native with netinfo dependency.

yarn add \
swr \
@nandorojo/swr-react-native@beta \
@react-native-community/netinfo
Enter fullscreen mode Exit fullscreen mode

Finally, let's add axios to handle your HTTP requests

yarn add axios
Enter fullscreen mode Exit fullscreen mode

Optionally, we can add typescript as development dependency:

yarn add typescript -D
Enter fullscreen mode Exit fullscreen mode

And create tsconfig.json file with

tsc --init 
Enter fullscreen mode Exit fullscreen mode

Step 3: Configure baseUrl:
We can create a folder named services in the root of our project with three files:

  • api.ts (or api.js if you don't installed typescript)
  • cities.ts (one entity where we're gonna request data)
  • neighborhood.ts (another entity where we're gonna request data)

In api file we're gonna use axios to indicate which url we'll use to request data:

import axios from 'axios';

const api = axios.create({
  baseURL: 'YOUR_BASE_URL'
})

export default api;
Enter fullscreen mode Exit fullscreen mode

On both remaining files let's write async functions that will make GET HTTP requests:

neighborhoods.ts

import api from './api';
const endpoint = 'YOUR_ENDPOINT'

async function getNeighborhoods(){
  const response = await api.get(endpoint)
  return response.data
}

export {
  getNeighborhoods
}
Enter fullscreen mode Exit fullscreen mode

You can write almost the same code in cities.ts file, just change the function name and the value of endpoint variable.

Step 4: Create screens
In our root project let's create pages folder with cities and neighborhoods folders inside it and those will contain files for css style, tsx code e one file to export our screen:

folder-structure

You can edit the .tsx and .css.ts files to style the screen the way you want and index.ts just to import and export our screen, like that:

import Neighborhoods from './Neighborhoods'
export default Neighborhoods;
Enter fullscreen mode Exit fullscreen mode

However, let's focus on the part that we use swr on Neighborhoods.tsx file.

Import swr and swr-react-native hooks:

import useSWR from 'swr';
import { useSWRNativeRevalidate } from "@nandorojo/swr-react-native";
Enter fullscreen mode Exit fullscreen mode

We need to import our async function from services folder too:

import { getNeighborhoods } from '../../services/neighborhoods';
Enter fullscreen mode Exit fullscreen mode

Inside our component function, let's create the fetcher variable that we'll pass as parameter to useSWR hook later:

const fetcher = () => getNeighborhoods().then(data => data);
Enter fullscreen mode Exit fullscreen mode

Basically, that variable is calling the asynchronous function and returning the data requested.

Now, let's understand the useSWR hook:

const { data: neighborhoods, mutate } = useSWR('your_endpoint', fetcher);
Enter fullscreen mode Exit fullscreen mode

That hook needs two parameters, the first one is a unique key for the request and in many cases the value will be the endpoint. The second one is a Promise to get our data, here we will pass the fetcher variable created previously.

Finally, let's include swr-react-native hook to handle revalidation and take advantage of revalidateOnFocus feature.

useSWRNativeRevalidate({ mutate });
Enter fullscreen mode Exit fullscreen mode

Leaving our code like this:

export default function Neighborhoods(){
 const fetcher = () => getNeighborhoods().then(data => data)
 const { data: neighborhoods, mutate } = useSWR('/v1/neighborhood', fetcher)
 useSWRNativeRevalidate({ mutate });
 return (
  // ...
 )
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Create navigators
In this step we'll create stack and tabs navigators. So let's begin with the tabs one:
First, create a folder in the project root named routes and inside it create Tabs.tsx file. We'll define which screen will be shown when a tab is pressed.

import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

// Screens
import Cities from '../pages/cities';
import Neighborhoods from '../pages/neighborhoods';

const { Navigator, Screen } = createBottomTabNavigator();

export default function Tabs() {
 return (
  <Navigator screenOptions={{ headerShown: false }}>
   <Screen
    name="cities"
    component={Cities}
   />
   <Screen
    name="neighborhoods"
    component={Neighborhoods}
   />
  </Navigator>
 )
}
Enter fullscreen mode Exit fullscreen mode

Create a Stack.tsx file in the same folder where we'll just indicate the Tabs component as screen:

import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import Tabs from './Tabs';

const { Navigator, Screen } = createStackNavigator();

export default function Stack(){
 return(
  <NavigationContainer>
   <Navigator
    screenOptions={{ headerShown: false }}
    initialRouteName="tabs"
   >
    <Screen name="tabs" component={Tabs} />
   </Navigator>
  </NavigationContainer>
 )
}
Enter fullscreen mode Exit fullscreen mode

Step 6: Importing stack navigator
From here, we already have our navigation done. But to use it we need to import the Stack component in App.tsx (or App.js) and configure some swr global parameters:

import React from 'react';
import Stack from './routes/Stack';
import { SWRConfig } from 'swr'

export default function App() {
 return (
  <SWRConfig
   // these will be passed as third parameter
   // of every useSWR hook used in our project
   value={{
    revalidateOnFocus: true,
    revalidateOnReconnect: true,
    revalidateIfStale: true,
    focusThrottleInterval: 5000
   }}
  >
   <Stack />
  </SWRConfig>
 );
};
Enter fullscreen mode Exit fullscreen mode

Step 7: Show the data
Going back to our Neighborhoods.tsx file, in the return we can map over the neighborhood variable:

import React from 'react';
import { useSWRNativeRevalidate } from '@nandorojo/swr-react-native';
import { ActivityIndicator, Text, View } from 'react-native';
import useSWR from 'swr';
import { getNeighborhoods } from '../../services/neighborhoods';
import styles from './Neighborhoods.css';

export default function Neighborhoods(){
 const fetcher = () => getNeighborhoods().then(data => data)
 const { data: neighborhoods, mutate } = useSWR('/v1/neighborhood', fetcher)
 useSWRNativeRevalidate({ mutate });

 return(
  <View style={styles.container}>
   {
    !neighborhoods ? <ActivityIndicator animating={true} size={'large'} /> :
    neighborhoods.map((neighborhood: any) => (
     <View key={neighborhood._id}>
      <Text style={styles.name}>
       {neighborhood.name}
      </Text>
      <Text style={styles.city}>
       {neighborhood.city.name}
      </Text>
     </View>
    ))
   }
  </View>
 )
}
Enter fullscreen mode Exit fullscreen mode

In the example above I'm showing the neighborhood name and the city name associated with it: check screenshot

Last step: Update the data and check the revalidation!
To see revalidateOnFocus in action, I updated the last neighborhood name using Postman and when neighborhood screen was in focus again, this was the result: check result gif

Check the repository with the code here
I would like to know your thoughts about this tutorial too, so feel free to comment!

Top comments (0)