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.value = 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 useStore
hook, which will load our store into memory so we can access our data
state atom via useValue
.
import { useStore, useValue } from "apps-digest"; import DataStore from "./stores/DataStore"; const DataComponent = () => { const dataStore = useStore(DataStore); const data = useValue(dataStore.data); return <div>{data === null ? "Loading..." : data.name}</div>; };
And voilà! Our data is loaded!
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.