I would like to avoid calling an API twice if I already have the data in my store.
How do I do this with Redux?
A simple and fast way to do it (although not recommended for anything scalable):
Use redux-persist to persist (cache) the store. Whenever it rehydrates, you know the data you had previously is present - read the docs in the link for how it works and how to setup.
To avoid unnessecary data-fetches on the remote server, you can cache the URLs (as Timos answer) to the localStorage or such, and simply check for its existence before doing the actual fetch.
Action:
function fetchUsers(url){
if(isCached(url)) {
// The data is already in the store, thanks to redux-persist.
// You can select it in the state as usual.
dispatch({ type: 'IS_CACHED', payload: url })
} else {
return fetch(url)
.json()
.then((response) => {
dispatch({ type: 'USERS_FETCH_SUCCESS', payload: response.data })
setCached(url)
})
.catch((error) => {
dispatch({ type: 'USERS_FETCH_FAILED', payload: error })
})
}
}
Simple custom-cache for urls:
const CACHE_NAME = 'MY_URL_CACHE'
export function getUrlCache() {
var localStorage
try {
localStorage = window.localStorage
// Get the url cache from the localstorage, which is an array
return ( localStorage.getItem(CACHE_NAME) ? JSON.parse(localStorage.getItem(CACHE_NAME)) : [] )
} catch(e){
// Make your own then...
throw "localStorage does not exist yo"
}
}
export function isCached(url) {
var urlCache = getUrlCache()
return urlCache.indexOf(url) !== -1
}
export function setCached(url){
if( isCached(url) )
return false
var urlCache = getUrlCache()
urlCache.push(url)
localStorage.setItem(CACHE_NAME, urlCache)
return true
}
export function removeCached(url){
var myCache = getUrlCache()
var index = myCache.indexOf(url)
if(index !== -1){
myCache = myCache.splice(index, 1)
localStorage.setItem(CACHE_NAME, urlCache)
return true
} else {
return false
}
}
You would also need to remove the cached url when/if the redux-persist data is flushed or some other thing that makes the data "old".
I recommend doing the whole thing using the redux store with persisting, and rather model the reducer/action logic on it. There are many ways to do it, and I highly recommend exploring redux, redux-saga and redux-persist and common concepts/design patterns.
Sidenote on basic example: You can also use redux-persist-transform-expire transformer for redux-persist to let cached data expire at some point in time, and modify it to remove the relevant cached url while doing so.