1
votes

I have a next.js application that uses the context api. However i am having what appears to be a hydration issue? But im unsure how to fix it.

next-dev.js?3515:32 Warning: Did not expect server HTML to contain the text node " " in . at div at eval (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-699e6908.browser.esm.js:55:66) at Box (webpack-internal:///./node_modules/@mui/system/esm/createBox.js:35:72) at Loading at AppProvider (webpack-internal:///./src/contexts/app/AppContext.tsx:177:9) at MyApp (webpack-internal:///./src/pages/_app.tsx:47:27) at StyleRegistry (webpack-internal:///./node_modules/styled-jsx/dist/stylesheet-registry.js:231:34) at ErrorBoundary (webpack-internal:///./node_modules/@next/react-dev-overlay/lib/internal/ErrorBoundary.js:26:47) at ReactDevOverlay (webpack-internal:///./node_modules/@next/react-dev-overlay/lib/internal/ReactDevOverlay.js:90:23) at Container (webpack-internal:///./node_modules/next/dist/client/index.js:331:9) at AppContainer (webpack-internal:///./node_modules/next/dist/client/index.js:770:26) at Root (webpack-internal:///./node_modules/next/dist/client/index.js:891:27)

The error suggests that it is something to do with my app context but im not sure what it is.

I have reproducible example of the code here.

https://codesandbox.io/s/interesting-mendel-d4dlu

  1. Login using 123, 123
  2. Refresh the page with the refresh button in the inbuilt codesandbox browser

View the console log error

AppContext

import React, { Component, ReactElement, createContext } from "react";
import { createTheme } from "@mui/material/styles";
import { IAuthenticateUser, Authenticate, VerifyAuthentication } from "../../services/AuthService";
import { Loading } from "../../components/loading/Loading";

export interface IAppProviderProps {}

export interface IAppProviderState {
    user: any | null;
    token: string | null;
    setUser?: any;
    isAuthenticated?: boolean;
    setDarkMode?: any;
    theme: any;
    isLoading?: boolean;
}

const DEFAULT_THEME = createTheme({
    typography: {
        fontSize: 12,
    },
    palette: {
        mode: "light",
        primary: {
            main: "#212121",
        },
        secondary: {
            main: "#fafafa",
        },
    },
});

const DARK_THEME = createTheme({
    typography: {
        fontSize: 12,
    },
    palette: {
        mode: "dark",
        primary: {
            main: "#212121",
        },
        secondary: {
            main: "#fafafa",
        },
    },
});

const INITIAL_STATE: IAppProviderState = { user: null, token: null, theme: DEFAULT_THEME, isAuthenticated: false };

export const AppContext = createContext(INITIAL_STATE);

export class AppProvider extends Component<IAppProviderProps, IAppProviderState> {
    constructor(props: IAppProviderProps) {
        super(props);

        const data = this.getUser();
        if (data) {
            this.state = {
                user: data.user,
                token: data.token,
                theme: INITIAL_STATE.theme,
                isLoading: true,
            };
        } else {
            this.state = INITIAL_STATE;
        }
    }

    componentDidMount = async () => {
        if (this.state.token) {
            const isAuthenticated = await this.isUserAuthenticated(this.state.token);
            this.setState({ isAuthenticated: isAuthenticated, isLoading: false });
        }
    };

    setUser = async (payload: IAuthenticateUser): Promise<boolean> => {
        const response = await Authenticate(payload);
        if (response && response.status === 200) {
            this.setState({
                user: response.data.user,
                token: response.data.token,
                isAuthenticated: true,
            });
            return true;
        }
        return false;
    };
    setDarkMode = async (toggle: boolean): Promise<void> => {
        const theme = toggle ? DARK_THEME : DEFAULT_THEME;
        this.setState({
            theme: theme,
        });
    };

    componentDidUpdate = (prevProps: IAppProviderProps, prevState: IAppProviderState) => {
        if (this.state.user !== prevState.user || this.state.token !== prevState.token) {
            console.log("didupdate");
            this.syncUser();
        }
    };

    syncUser = () => {
        localStorage.setItem("token", JSON.stringify(this.state.token));
        localStorage.setItem("user", JSON.stringify(this.state.user));
    };

    getUser = () => {
        try {
            const user = localStorage.getItem("user");
            const token = localStorage.getItem("token");

            if (!user || !token) {
                return null;
            }

            const response = {
                user: JSON.parse(user),
                token: JSON.parse(token),
            };

            return response;
        } catch (err) {
            return null;
        }
    };

    isUserAuthenticated = async (token: any): Promise<boolean> => {
        try {
            const response = await VerifyAuthentication(token);
            if (response.status !== 200) {
                return false;
            }

            return true;
        } catch (err) {
            return false;
        }
    };
    render(): ReactElement {
        if (this.state.isLoading) {
            return <Loading />;
        } else {
            return (
                <AppContext.Provider
                    value={{
                        theme: this.state.theme,
                        user: this.state.user,
                        setUser: this.setUser,
                        setDarkMode: this.setDarkMode,
                        token: this.state.token,
                        isAuthenticated: this.state.isAuthenticated,
                        isLoading: this.state.isLoading,
                    }}
                >
                    {this.props.children}
                </AppContext.Provider>
            );
        }
    }
}
Upon further investigatin i think its something to do with my render here if (this.state.isLoading) { return <Loading />; } else {Kay