Проблема с сохранением глобального состояния ReactJS

#reactjs #local-storage #global-state

Вопрос:

Я работаю над проектом, в котором я реализовал глобальное состояние, используя то, что доступно в ReactJS, сторонняя библиотека не используется.

Когда я устанавливаю глобальное состояние, я затем также добавляю его в локальное хранилище, поэтому, если браузер перезагрузится, идея состоит в том, что я смогу извлечь данные из локального хранилища и перевести их в глобальное состояние, но, похоже, это не работает, поскольку я продолжаю получать неопределенное ошибка при попытке получить доступ к свойству в глобальном состоянии после перезагрузки страницы.

Мой GlobalStateProvider выглядит следующим образом:

 import React, {createContext, useState, useContext, Dispatch, SetStateAction, useEffect} from "react";
export interface GlobalStateInterface {
    trial: {
        in_trial: boolean,
        days_of_trial_remaining: number
    }
    payment_failed: {
        last_payment_failed: boolean,
        payment_time_required: number,
        last_payment_failed_reason: string,
        payment_required: boolean
    }
}

const GlobalStateContext = createContext({
    globalState: {} as Partial<GlobalStateInterface>,
    setGlobalState: {} as Dispatch<SetStateAction<Partial<GlobalStateInterface>>>,
});

const GlobalStateProvider = ({
                                 children,
                                 value = {} as GlobalStateInterface,
                             }: {
    children: React.ReactNode;
    value?: Partial<GlobalStateInterface>;
}) => {
    const [globalState, setGlobalState] = useState(value);

    return (
        <GlobalStateContext.Provider value={{ globalState, setGlobalState }}>
            {children}
        </GlobalStateContext.Provider>
    );
};

const useGlobalState = () => {
    const context = useContext(GlobalStateContext);
    if (!context) {
        throw new Error("useGlobalState must be used within a GlobalStateContext");
    }
    return context;
};

export { GlobalStateProvider, useGlobalState };
 

Я создал базовый компонент, который, как я ожидал, я мог бы использовать, чтобы проверить, содержит ли localStorage поставщика глобального состояния, и если да, то загрузить его в глобальное состояние, поэтому этот компонент содержит следующее:

 import * as React from "react";
import {useEffect} from "react";
import {useGlobalState} from "./GlobalStateProvider";

export default function BaseComponent(props) {

    const { setGlobalState } = useGlobalState();
    useEffect(() => {

        let tempGlobalStorage = localStorage.getItem("global_state");

        console.log("Temp Global State: ", tempGlobalStorage);
        if (tempGlobalStorage !== null) {
            const tempGlobalStorageJson = JSON.parse(tempGlobalStorage);
            setGlobalState({...tempGlobalStorageJson});
        }

    }, []);

    return (
        <>
            {props.children}
        </>
    )
}
 

My App.js looks like the following: (I’m only showing what is being returned in App.js as I don’t think anything is relevant)

 return (
        <Elements stripe={stripePromise}>
            <GlobalStateProvider>
                <BaseComponent>
                    <BrowserRouter>
                        <div className='h-full'>
                            <Switch>
                                <Route path="/" render={(props) => <Login {...props}  /> } exact />
                                <Route path="/login/:msg?/:redirect?" render={(props) => <Login {...props} /> } exact />
                                <Route path="/choose-plan" render={() => <ChoosePlan /> } exact />
                                <Route path="/signup/:plan" render={(props) => <SignUp {...props} /> } exact />
                                <Route path="/dashboard" render={() => <Dashboard  /> } exact />
                                <Route path="/project-settings/:project_id" render={(props) => <ProjectSettings {...props} /> } />
                                <Route path="/settings" render={() => <Settings /> } exact />
                                <Route path='/add-project' render={() => <AddProject  /> } exact />
                                <Route path='/final-project-setup/:platform/:project_id/:api_key' render={(props) => <FinalProjectSetup {...props}  /> } />
                                <Route path='/crash-details/:project_id/:project_type/:crash_group_id?/:comment_id?' render={(props) => <CrashDetails {...props} /> } />
                                <Route path='/team-members' render={() => <ManageTeamMembers  /> } />
                                <Route path='/add-team-member' render={() => <AddTeamMember  /> } />
                                <Route path='/register-new-user/:invite_params?' render={(props) => <RegisterNewUser {...props} /> } />
                                <Route path='/account-billing' render={() => <AccountAndBilling /> } exact />
                                <Route path='/forgotten-password' render={() => <ForgottenPassword /> } exact />
                                <Route path='/reset-password/:resetPasswordData' render={(props) => <ResetPassword {...props} /> } />

                                <Route path='/status' render={() => <Status /> } />
                                <Route render={() => <NotFound  />}/>
                            </Switch>
                        </div>
                    </BrowserRouter>
                </BaseComponent>
            </GlobalStateProvider>
        </Elements>
    );
 

Как вы можете видеть в базовом компоненте, я выхожу из того, что находится в localStorage, оно ничего не выводит при перезагрузке страницы, как будто я получаю доступ к глобальному состоянию в одном из компонентов маршрута, срабатывает до того, как базовый компонент запускает useEffect.

Ниже приведен пример, в котором задается состояние и глобальное хранилище:

 const tempGlobalState = globalState;
                tempGlobalState.trial = {
                    in_trial: response.data.in_trial,
                    days_of_trial_remaining: response.data.in_trial ? response.data.trial_days_remaining : null
                }

                tempGlobalState.payment_failed = {
                    last_payment_failed: response.data.last_payment_failed,
                    last_payment_failed_reason: response.data.last_payment_failed_reason,
                    payment_time_required: response.data.payment_time_required,
                    payment_required: false //Still within the 7 day grace period for non payment
                }
                /*tempGlobalState.trial.in_trial = response.data.in_trial;
                if (response.data.in_trial)
                {
                    tempGlobalState.trial.days_of_trial_remaining = response.data.trial_days_remaining;
                }*/
                localStorage.setItem("global_state", JSON.stringify(tempGlobalState));
                setGlobalState({...tempGlobalState});
 

Комментарии:

1. Где вы устанавливаете значение global_state ? Если вы никогда не устанавливали его в localStorage, то вы не можете ожидать, что он вернет что-либо при перезагрузке.

2. Я, очевидно, проверил, что локальное хранилище имеет значение, установленное в инструментах разработчика Chrome. Но я добавил к вопросу пример его хранения в локальном хранилище.

3. Хм … может быть, ваше локальное хранилище находится в плохом состоянии. Я смог заставить ваш код работать с упрощенной версией GlobalState: codesandbox.io/s/stoic-pine-rnrb8 Ваша основная идея здрава. Это может помочь максимально упростить устранение неполадок…

4. Странно, codesandbox каким-то образом был поврежден. Если это будет полезно, я могу попытаться восстановить его. LMK.

5. Мне удалось заставить его работать. Похоже, что useEffect не срабатывал, я реализовал его до возврата компонента и проверил, что состояние еще не установлено, а если нет, то извлеките его из localstorage и установите глобальное хранилище. Но все же спасибо за вашу помощь