Реагируйте, контекст и хранилище сеансов: панель навигации обновляется правильно при входе в систему, но не при выходе из системы

#javascript #reactjs #navigationbar #react-context #session-storage

Вопрос:

У меня есть две вещи, которые я хотел бы знать: во-первых, панель навигации моего приложения неправильно обновляется на гостевой панели навигации при выходе из системы, но обновляется на панели навигации пользователя, когда в нее входит учетная запись, не являющаяся администратором. Если я обновлю страницу (F5) после выхода из системы, она обновится обратно на гостевую навигационную панель, но это противоречит цели того, чтобы это было SPA (одностраничное приложение).

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

auth.js:

 import { createContext, useContext } from "react";

export const AuthContext = createContext();

export function useAuth() {
    return useContext(AuthContext);
}
 

App.js:

 import { useState, useEffect } from "react";
import { BrowserRouter as Router } from "react-router-dom";
import { AuthContext } from './context/auth';
import "bootstrap/dist/css/bootstrap.min.css";

/* Begin Import Components */
import NavigationBar from "./components/navbar";
import Public from "./components/public-components/public";
/* End Import Components */

function App(props) {
  const [currentUser, setCurrentUser] = useState();

  // Temporary for testing
  const [currentTime, setCurrentTime] = useState(0);
  useEffect(() => {
    fetch('/time').then(res => res.json()).then(data => {
      setCurrentTime(data.time);
    });
  }, []);

  const setAuth = (data) => {
    if(data) {
        sessionStorage.setItem('username', data.username);
        sessionStorage.setItem('isAdmin', data.isAdmin);
        setCurrentUser({username: data.username, isAdmin: data.isAdmin});
        console.log('Data in setAuth: ', data);
    } else {
        sessionStorage.setItem('username', null);
        sessionStorage.setItem('isAdmin', null);
    }
  }

  return (
    <AuthContext.Provider value={{ currentUser, setCurrentUser: setAuth }}>
      <Router>
        <NavigationBar />
        <div className="container text-center">
          <Public />
          <p>The current time is {currentTime}.</p>
        </div>
      </Router>
      </AuthContext.Provider>
  );
}

export default App;
 

navbar.js:

 import { Navbar, Nav, Container } from 'react-bootstrap';
import { Link } from "react-router-dom";

function Navigation(props) {
    const username = sessionStorage.getItem('username');
    const isAdmin = sessionStorage.getItem('isAdmin');
    const styledNavLink = {
        textDecoration: "none",
        color: "#F5F5F5",
        marginLeft: "10px",
        marginTop: "5px"
    }
    const styledHeader = {
        textDecoration: "none",
        color: "#FFF",
        fontSize: "1.5em"
    }

    function adminNav() {
        return (
            <Navbar.Collapse id="basic-navbar-nav">
                <Nav className="me-auto">
                    <Link to="#" style={styledNavLink}>Admin</Link>
                </Nav>
                <Nav>
                    <Link to={`/${username}`}>Welcome, {username}</Link>
                    <Link to="/logout" style={styledNavLink}>Log Out</Link>
                </Nav>
            </Navbar.Collapse>
        )
    }

    function loggedInNav() {
        return (
            <Navbar.Collapse id="basic-navbar-nav">
                <Nav className="me-auto">
                    <Link to="#" style={styledNavLink}>User</Link>
                </Nav>
                <Nav>
                    <Link to={`/${username}`} style={styledNavLink}>Welcome, {username}</Link>
                    <Link to="/logout" style={styledNavLink}>Log Out</Link>
                </Nav>
            </Navbar.Collapse>
        )
    }

    function guestNav() {
        return(
            <Navbar.Collapse id="basic-navbar-nav">
                <Nav className="me-auto">
                    <Link to="#" style={styledNavLink}>Guest</Link>
                </Nav>
                <Nav>
                    <Link to="/register" style={styledNavLink}>Register</Link>
                    <Link to="/login" style={styledNavLink}>Login</Link>
                </Nav>
            </Navbar.Collapse>
        )
    }

    return (
        <Navbar bg="primary" variant="dark" expand="md">
            <Container>
                <Link to="/" style={styledHeader}>Flaskagram</Link>
                <Navbar.Toggle aria-controls="basic-navbar-nav" />
                {username !== 'null'
                ?
                    isAdmin === 'true'
                    ?
                    adminNav()
                : loggedInNav()
                : guestNav()
                }
            </Container>
        </Navbar>
    )
}

export default Navigation;
 

login.js:

 import { useState } from 'react';
import { Link, Redirect } from 'react-router-dom';
import { useAuth } from '../../context/auth';
import { Col, Form, Row, Button } from 'react-bootstrap';

function Login(props) {
    const [loggedIn, setLoggedIn] = useState(false);
    const [isError, setIsError] = useState(false);
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const { setCurrentUser } = useAuth();

    let referer;
    if(props.location.state !== undefined) {
        referer = props.location.state.referer;
    } else {
        referer = "/";
    }

    function postLogin() {
        const formData = new FormData();
        formData.append('email', email);
        formData.append('password', password);

        fetch('/login', {
            method: 'post',
            body: formData
        }).then(res => res.json())
        .then(data => {
            if(data.wrongPassword) {
                setIsError(true);
            } else {
                setCurrentUser({username: data.user.username, isAdmin: data.user.isAdmin});
                setLoggedIn(true);
            }
        }).catch(err => {
            console.log(err);
            setIsError(true);
        });;
    }

    if(loggedIn) {
        return <Redirect to={referer} />;
    }

    return (
        <main>
            <header>
                <h2>Login</h2>
            </header>
            <section>
                <Form>
                    <Row className="justify-content-sm-center">
                        <Form.Group as={Col} sm={{ span: 6 }}>
                            <Form.Label htmlFor="email">Email</Form.Label>
                            <Form.Control
                                        controlid="email"
                                        type="email"
                                        value={email}
                                        onChange={e => {
                                            setEmail(e.target.value);
                                        }}
                                        placeholder="Enter email"
                                        autoFocus
                                        />
                        </Form.Group>
                    </Row>
                    <Row className="justify-content-sm-center">
                        <Form.Group as={Col} sm={{ span: 6 }}>
                            <Form.Label htmlFor="password">Password</Form.Label>
                            <Form.Control
                                        controlid="password"
                                        type="password"
                                        value={password}
                                        onChange={e => {
                                            setPassword(e.target.value);
                                        }}
                                        placeholder="Enter password"
                                        />
                        </Form.Group>
                    </Row>
                    <Button onClick={postLogin} variant="success">Login</Button>
                </Form>
            </section>
            <Link to="/register">Don't have an account?</Link>
            { isError amp;amp;<p>There was a problem logging in!</p> }
        </main>
    )
}

export default Login;
 

logout.js:

 import { useState, useEffect } from 'react';
import { Redirect } from 'react-router-dom';
import { useAuth } from '../../context/auth';

function Logout(props) {
  const [isLoggedIn, setisLoggedIn] = useState(true);
  const { setCurrentUser } = useAuth();

  useEffect(() => {
    setCurrentUser();
    setisLoggedIn(false);
  }, [ setCurrentUser ]);

  if(!isLoggedIn) {
    return <Redirect to={"/"} />;
  }

  return (
    <div></div>
  );
}

export default Logout;
 

Ответ №1:

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

 import { useState, useEffect } from 'react';
import { Redirect } from 'react-router-dom';
import { useAuth } from '../../context/auth';

function Logout(props) {
  const [isLoggedIn, setisLoggedIn] = useState(true);
  const { setCurrentUser } = useAuth();

  useEffect(() => {
    setCurrentUser();
    /* remove session storage items */
    sessionStorage.removeItem('username');
    sessionStorage.removeItem('isAdmin');
    setisLoggedIn(false);
  }, [ setCurrentUser ]);

  if(!isLoggedIn) {
    return <Redirect to={"/"} />;
  }

  return (
    <div></div>
  );
}

export default Logout;
 

В вашем navBar.js файле, чтобы проверить, является ли userName он пустым, затем используйте !!userName , чтобы проверить, является ли он null пустым или undefined .

Поэтому обновите return Navigation компонент navBar.js файла следующим образом.

     return (
        <Navbar bg="primary" variant="dark" expand="md">
            <Container>
                <Link to="/" style={styledHeader}>Flaskagram</Link>
                <Navbar.Toggle aria-controls="basic-navbar-nav" />
                {!!username
                ?
                    isAdmin === 'true'
                    ?
                    adminNav()
                : loggedInNav()
                : guestNav()
                }
            </Container>
        </Navbar>
    )
 

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

1. К сожалению, после того, как я применил две новые строки кода, проблема все еще оставалась той же

2. Можете ли вы сказать мне, что navBar он показывает, когда пользователь выходит из системы, как вы говорите в своей проблеме ? Я считаю, что это должно быть или adminNav или loggedInNav ? И я также обнаружил еще одну проблему в Navigation компоненте.

3. Не могли бы вы, пожалуйста, проверить, что я обновил, и сказать мне, работает это или нет ? А также скажите мне, что navBar вы видите, когда выходите из системы, если проблема все еще существует.

4. Я обновил «имя пользователя» в разделе return() до «!!имя пользователя», но у меня все еще та же проблема. Когда я впервые начинаю выходить из системы, появляется гостевая навигационная панель, а хранилище сеансов пусто, как и предполагалось. После того, как я войду в систему, страница перейдет со страницы входа на корневую страницу, появится панель навигации для входа в систему, хранилище сеансов установит «имя пользователя» на «пользователь» и установит «isAdmin» на «ложь». Когда я выхожу из системы, хранилище сеансов отключается и возвращается на корневую страницу, но панель навигации, вошедшая в систему, остается, если я не обновлю страницу полностью. Только после этого он снова правильно обновится на гостевой навигационной панели

5. Нашел проблему. Посмотрите на ответ, который я опубликовал ниже.

Ответ №2:

Нашел проблему. Проблема заключалась setAuth() в том, что App.js. Я не обновлял контекст при выходе из системы (когда data не было определено в инструкции if/else), поэтому панель навигации оставалась в состоянии входа в систему из-за этого.

обновлено значение setAuth в App.js:

 const setAuth = (data) => {
    if(data) {
        sessionStorage.setItem('username', data.username);
        sessionStorage.setItem('isAdmin', data.isAdmin);
        setCurrentUser({username: data.username, isAdmin: data.isAdmin});
    } else {
      sessionStorage.removeItem('username');
      sessionStorage.removeItem('isAdmin');
      setCurrentUser();
    }
  }