Search code examples
javascriptreactjsreact-hooksglobal-variablesreact-context

Changing Navbar depending on globalstate boolean gives white screen


So I was following this tutorial to use global state through react context api.

I want to change the navbar based on if the user is logged in, where I need to use global state. But I am getting a white screen so I assume I did something wrong.

AppContext.js

import React from "react";
// ./components/AppContext.js
const AppContext = React.createContext();

export default AppContext;

Index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import reportWebVitals from './reportWebVitals';
import { CookiesProvider } from 'react-cookie';
import App from "./App";
import AppContext from "./components/AppContext";
// index.js
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <React.StrictMode>
        <CookiesProvider>
            <App/>
        </CookiesProvider>
    </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: 
reportWebVitals();

App.js

import React, {useState} from 'react';
import './App.css';
import Home from './Home';
import {BrowserRouter as Router, Route, Routes} from 'react-router-dom';
import User from "./User";

import NavBarSI from "./components/NavBarSignedIn";
import NavBarGL from "./components/NavBarGeneral";
import AppContext from "./components/AppContext";

//App.js
const App = () => {
    const userSettings = {
        setting2name: setting2value,
        setSetting2value
    };
    const [setting2value, setSetting2value] = useState(false);


    const navbar = setting2value ?
        <NavBarSI/> : <NavBarGL/>

    return (

        <Router>
            <AppContext.Provider value={userSettings}>
                {navbar}
                <NavBarSI/>
                <div>
                    <Routes>
                        <Route exact path="/" element={<Home/>}/>
                        <Route exact path="/user" element={<User/>}/>
                    </Routes>
                </div>
            </AppContext.Provider>
        </Router>

    );


}

export default App;

Home.js example where I change the boolean to true when user logs in.

import React, {useContext, useEffect, useState} from 'react';
import './App.css';
import { Button, Container } from 'reactstrap';
import { useCookies } from 'react-cookie';
import AppContext from "./components/AppContext";


// Home.js
const Home = () => {

    const myContext = useContext(AppContext);
    const [authenticated, setAuthenticated] = useState("authenticated");
    const [loading, setLoading] = useState(false);
    const [user, setUser] = useState(undefined);
    const [cookies] = useCookies(['XSRF-TOKEN']);



    useEffect(() => {
        setLoading(true);
            fetch('/user', { credentials: 'include' })
            .then(response => response.text())
            .then(body => {
                if (body === '') {
                    setAuthenticated(false);


                } else {
                    setUser(JSON.parse(body));
                    setAuthenticated(true);
                    myContext.setSetting2value(true);

                }
                setLoading(false);
            });
    }, [setAuthenticated, setLoading, setUser])

    const login = () => {
        let port = (window.location.port ? ':' + window.location.port : '');
        if (port === ':3000') {
            port = ':8080';
        }
        window.location.href = `//${window.location.hostname}${port}/private`;
    }

    const logout = () => {
        fetch('/user/logout', {
            method: 'POST', credentials: 'include',
            headers: { 'X-XSRF-TOKEN': cookies['XSRF-TOKEN'] }
        })
            .then(res => res.json())
            .then(response => {
                window.location.href = `${response.logoutUrl}&returnTo=${window.location.origin}`;
            });
    }

    const message = user ?
        <h2>Welcome, {user.name}!</h2> :
        <p>Please log in to manage your JUG Tour.</p>;

    const button = authenticated ?
        <div>
            <br/>
            <Button color="link" onClick={logout}>Logout</Button>
        </div> :
        <Button color="primary" onClick={login}>Login</Button>;



    if (loading) {
        return <p>Loading...</p>;
    }


    return (
        <div>
            <Container fluid>
                {message}
                {button}
            </Container>
        </div>
    );
}


export default Home;


Solution

  • Check your console.

    You getting white screen because trying to assign variable before its declaration.

    Swap variables. Should do the trick

    const [setting2value, setSetting2value] = useState(false);
    const userSettings = {
                setting2name: setting2value,
                setSetting2value
            };
    

    Furthermore i suggest to memoize your context object to prevent additional re-renders using useMemo.

    Like this:

        const context = useMemo(() => {
        const ctx = { setting2name: setting2value,setSetting2value}
        return ctx;
        }, [setting2name]);
    

    Hope it helps!