A better way to fetch data in React using App's Digest

Fetching data in react components can be done in multiple ways; unfortunately some of those are hard to maintain and debug. Let's review a common one.

The useEffect nightmare

The usual convention is to use the useEffect to trigger a fetch, to then set the data in a local state.

const DataComponent = () => { const [data, setData] = useState(null); useEffect(() => { const fetchData = async () => { const response = await fetch(`https://swapi.dev/api/people/1/`); const newData = await response.json(); setData(newData); }; fetchData(); }, []); return data && <div>{data.name}</div>; }

This data is only available in this component. If we need to access this data from any other component we'd need to setup some form of context, prop-drilling or state management.

There's also the risk of causing the useEffect to re-run more often than needed (read more about the useEffect caveats).

A new hope

With the state management approach in mind, let's review another way to fetch data using App's Digest.

Setting up our store

Let's create a new file within our project src/stores/DataStore.js. And let's put in place the basic store setup.

import { AppsDigestValue } from "apps-digest"; class DataStore { data = new AppsDigestValue(null); } export default DataStore;

Adding a fetching method

Let's create an async method called fetchData, which will do the same fetching call we saw in the useEffect example. And in order to set the state, we can just publish the new data from our data state atom.

class DataStore { data = new AppsDigestValue(null); async fetchData() { const response = await fetch(`https://swapi.dev/api/people/1/`); const newData = await response.json(); this.data.publish(newData); } }

Trigger our fetching from our constructor

Now, we want to make sure that everytime our store is loaded, the data that it needs is fetched. We can achieve this by calling our new method from the constructor

class DataStore { data = new AppsDigestValue(null); constructor() { this.fetchData(); } async fetchData() {...} }

Use our new store in our component

Now let's use our store in our component and see the data loading. We will consume our store via useAppsDigestStore hook, which will load our store into memory so we can access our data state atom via useAppsDigestValue.

import { useAppsDigestStore, useAppsDigestValue } from "apps-digest"; import DataStore from "./stores/DataStore"; const DataComponent = () => { const dataStore = useAppsDigestStore(DataStore); const data = useAppsDigestValue(dataStore.data); return <div>{data === null ? "Loading..." : data.name}</div>; };

And voilà! Our data is loaded!

apps-digest-data-fetching.gif

With this approach we can delegate the data fetching to our store and keep our components clean. This keeps our business logic separate, which is easier to test, maintain and debug.

Resources