How do I implement caching in Redux?

前端 未结 6 2076
难免孤独
难免孤独 2020-12-24 12:17

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?

6条回答
  •  粉色の甜心
    2020-12-24 12:33

    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.

提交回复
热议问题