首页 > 解决方案 > Gatsbyjs & localStorage: Persisting state

问题描述

I'm building a GDPR banner for my site. I want this banner to appear on every page of my site until the user clicks the accept button. In my Gatsby site I am using the Context API to store whether the user has accepted the terms with a piece of state in local storage called GDPR_Accepted. I initialize it to false and when the user clicks the accept button in the GDPR banner I toggled it to true. After it is set to true the GDPR banner should not be shown on any page.

I have this functionality working, except when the user leaves my site and comes back GDPR_Accepted resets to false, and the GDPR banner is shown again even though the user had previously accepted the terms. Why isn't localStorage persisting the data when the user leaves?

Here is my Context API code where I handle the GDPR_Accepted state:

function reducer(state, action) {
    switch(action.type) {
        case TOGGLE_GDPR: {
            return {
                ...state,
                GDPR: state.GDPR === false ? true : false
            }
        }
        default: {
            return {
                ...state
            }
        }
    }
}

export const GlobalContextProvider = ({ children }) => {
    const [state, dispatch] = React.useReducer(reducer, initialState)

    useEffect(() => {
        if (storageAvailable('localStorage')) {
            localStorage.setItem('GDPR_Accepted', state.GDPR)
          }
          else {
            return 0;
          }
    }, [state])

    return (
        <GlobalStateContext.Provider value={state}>
            <GlobalDispatchContext.Provider value={dispatch}>
                {children}
            </GlobalDispatchContext.Provider>
        </GlobalStateContext.Provider>
    )
}

I also have this function I took from MDN docs that first checks is local storage is available:

// Checks if localStorage is available in the user's browser
   function storageAvailable(type) {
    var storage;
    try {
        storage = window[type];
        var x = '__storage_test__';
        storage.setItem(x, x);
        storage.removeItem(x);
        return true;
    }
    catch(e) {
        return e instanceof DOMException && (
            // everything except Firefox
            e.code === 22 ||
            // Firefox
            e.code === 1014 ||
            // test name field too, because code might not be present
            // everything except Firefox
            e.name === 'QuotaExceededError' ||
            // Firefox
            e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
            // acknowledge QuotaExceededError only if there's something already stored
            (storage && storage.length !== 0);
        }
    }

In my frontend I have a component that displays the actual GDPR banner that appears, inside this component I toggle the GDPR_Accepted state being stored in localStorage with this:

 <Button className={styles.acceptButton} onClick={async () => {
        dispatch({ type: "TOGGLE_GDPR" })
 }}>Accept All Cookies</Button>

Then, in my Layout component, I use a ternary to decide if the GDPR banner should be displayed or not. I access the state from the Context API, const state = useContext(GlobalStateContext); and then use the ternary:

{
   state.GDPR === false ?                    
    <GDPR />
   : null
}

All of this works, but I have the issue with persisting the state when the user leaves and comes back. Some possible issues that have crossed my mind:

标签: javascriptlocal-storagegatsbyreact-context

解决方案


看起来您useEffect在 GlobalContextProvider 定义中的钩子正在重置 localStorage 键的值。每次安装组件(即每次加载页面)时,该功能将(至少)运行。无需在每次页面加载时初始化 localStorage 键。您调用的唯一位置setItem应该在按钮的onClick.


推荐阅读