Data fetching in React using App's Digest
Tags: react, webdev, javascript
Published: 2023-02-18 03:09:34

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.

```js
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](https://beta.reactjs.org/reference/react/useEffect#caveats)).

## A new hope

With the state management approach in mind, let's review another way to fetch data using [App's Digest](https://github.com/martyroque/apps-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. 

```js
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.

```js
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

```js
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`.

```js
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](https://flwcyqjauqbtlhkhiugl.supabase.co/storage/v1/object/public/images/notes/4iiA7Xq8VexypXjfAbhkc_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

- [Full CodeSandbox](https://codesandbox.io/s/apps-digest-data-fetching-s9gfyd)
- [App's Digest Github](https://github.com/martyroque/apps-digest)
- [App's Digest NPM Package](https://www.npmjs.com/package/apps-digest)