Веб-приложению требуется слишком много времени, чтобы определить, аутентифицирован ли пользователь или нет - PullRequest
1 голос
/ 09 января 2020

Справочная информация

Я только начал использовать / изучаю, как использовать Next. js, и я столкнулся с проблемой, когда мой логин аутентификации пользователя c заставляет мои компоненты долго сделать на странице Я считаю, что мне не хватает чего-то действительно фундаментального, и я не уверен, связано ли это с Next. js или как-то связано с тем, как я обрабатываю пользовательские состояния в своем приложении.

Я использую Firebase Google Authentication для обработки моего имени пользователя.

Код, на который я буду ссылаться в моем вопросе, существует в следующем репозитории: https://github.com/myamazingthrowaway/nextjswebsite

Живая демонстрация Приложение можно найти здесь: https://nextjswebsite-kappa-sand.now.sh/

(он использует межсайтовые файлы cookie для обработки входа в Google с помощью firebase - я не знаю, как изменить это поведение по умолчанию, поэтому, если оно не работает в первый раз, убедитесь, что ваш браузер разрешает использование межсайтовых файлов cookie)

Я основал мои аутентификационные логи c на следующем репозитории: https://github.com/taming-the-state-in-react/nextjs-redux-firebase-authentication

Мое веб-приложение было создано с create-next-app.

Проблема

Когда пользователь посещает мой веб-сайт, компонент боковой панели и остальные компоненты, которые зависят от состояния пользователя, вошедшего в систему, не отображаются сразу при загрузке страницы. Они появляются через некоторое время после рендеринга начальной страницы. Это значительная задержка, и на моей вкладке chrome нет «индикатора загрузки», чтобы сказать, что дом все еще строится. Это ожидаемое поведение?

Эту проблему также можно увидеть на следующем сайте (вход в Google демонстрирует, что я имею в виду). Действия по воспроизведению: 1. Go до: https://kage.saltycrane.com/ 2. Нажмите «Войти» 3. Нажмите «Войти через Google» Вы будете перенаправлены на страницу входа в Google, выберите учетную запись et c. (чтобы войти) Затем вы будете перенаправлены обратно на сайт на шаге 1, где строка меню в верхней части все еще остается «Войти» ... на мгновение или два, прежде чем она изменится на ваш адрес электронной почты.

Почему это случилось?

(код выше веб-страницы здесь: https://github.com/saltycrane/kage)

Мой код

В моем файле _app.js, У меня есть компонент «Shell», который обрабатывает мою боковую панель и панель навигации для всего веб-приложения. Он допускает отображение дочерних компонентов в пределах боковой панели et c. Возможно, это не лучший способ справиться с тем, как работает приложение (был бы рад получить предложения о том, как это улучшить).

Файл _app.js выглядит следующим образом:

import React from "react";
import App from "next/app";

import CssBaseline from "@material-ui/core/CssBaseline";
import { ThemeProvider } from "@material-ui/styles";

import { Provider } from "react-redux";
import withRedux from "next-redux-wrapper";

import initStore from "../src/store";
import theme from "../src/theme";

import Shell from "../src/components/Shell";

class EnhancedApp extends App {
  static async getInitialProps({ Component, ctx }) {
    return {
      pageProps: Component.getInitialProps
        ? await Component.getInitialProps(ctx)
        : {}
    };
  }

  componentDidMount() {
    const jssStyles = document.querySelector("#jss-server-side");

    if (jssStyles) {
      jssStyles.parentNode.removeChild(jssStyles);
    }
  }

  render() {
    const { Component, pageProps, store } = this.props;
    return (
      <>
        <Provider store={store}>
          <ThemeProvider theme={theme}>
            <title>Next.js</title>
            <CssBaseline />
            <Shell>
              <Component {...pageProps} />
            </Shell>
          </ThemeProvider>
        </Provider>
      </>
    );
  }
}

export default withRedux(initStore)(EnhancedApp);

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

import React from "react";
import Router from "next/router";
import { connect } from "react-redux";

import {
  Drawer,
  List,
  Divider,
  ListItem,
  ListItemIcon,
  ListItemText,
  Hidden,
  AppBar,
  Toolbar,
  IconButton,
  Button
} from "@material-ui/core";
import { ProfileIcon } from "../index";

import MonetizationOnOutlinedIcon from "@material-ui/icons/MonetizationOnOutlined";
import AccountBalanceWalletRoundedIcon from "@material-ui/icons/AccountBalanceWalletRounded";
import AccountBoxRoundedIcon from "@material-ui/icons/AccountBoxRounded";
import VpnKeyRoundedIcon from "@material-ui/icons/VpnKeyRounded";
import ExitToAppRoundedIcon from "@material-ui/icons/ExitToAppRounded";
import MenuIcon from "@material-ui/icons/Menu";

import { makeStyles } from "@material-ui/core/styles";

import * as routes from "../../constants/routes";
import { auth } from "../../firebase/firebase";

const drawerWidth = 180;

const useStyles = makeStyles(theme => ({
  content: {
    flexGrow: 1,
    padding: theme.spacing(3)
  },
  root: {
    display: "flex"
  },
  container: {
    flexGrow: 1
  },
  toolbar: theme.mixins.toolbar,
  drawer: {
    [theme.breakpoints.up("md")]: {
      width: drawerWidth,
      flexShrink: 0
    }
  },
  drawerPaper: {
    width: drawerWidth
  },
  appBar: {
    background: "linear-gradient(45deg,  #FF8E53 30%, #ff4d73 90%)",
    marginLeft: drawerWidth,
    [theme.breakpoints.up("md")]: {
      width: `calc(100% - ${drawerWidth}px)`
    }
  },
  logoContainer: {
    background: "linear-gradient(45deg, #ff4d73 30%, #FF8E53 90%)",
    justifyContent: "center",
    flexDirection: "column",
    height: "15rem"
  },
  menuButton: {
    marginRight: theme.spacing(2),
    [theme.breakpoints.up("md")]: {
      display: "none"
    }
  },
  rightAlign: {
    marginLeft: "auto",
    marginRight: -12,
    cursor: "pointer"
  },
  hoverCursor: {
    cursor: "pointer"
  }
}));

const Shell = ({ children, authUser }) => {
  const classes = useStyles();
  const [mobileOpen, setMobileOpen] = React.useState(false);

  const handleGoToEarnPage = () => {
    Router.push(routes.EARN);
    if (mobileOpen) handleDrawerToggle();
  };

  const handleGoToSignInPage = () => {
    Router.push(routes.SIGN_IN);
    if (mobileOpen) handleDrawerToggle();
  };

  const handleGoToWithdrawPage = () => {
    Router.push(routes.WITHDRAW);
    if (mobileOpen) handleDrawerToggle();
  };

  const handleGoToProfilePage = () => {
    Router.push(routes.PROFILE);
    if (mobileOpen) handleDrawerToggle();
  };

  const handleDrawerToggle = () => {
    setMobileOpen(!mobileOpen);
  };

  const handleGoToHomePage = () => {
    Router.push(routes.LANDING);
    if (mobileOpen) handleDrawerToggle();
  };

  const handleSignOut = () => {
    auth.signOut();
    if (mobileOpen) handleDrawerToggle();
  };

  const drawer = (
    <>
      <AppBar position="static">
        <Toolbar className={classes.logoContainer}>
          <img
            src="/images/logo/logo.png"
            alt="my logo"
            height="120rem"
            onClick={handleGoToHomePage}
            className={classes.hoverCursor}
          />
        </Toolbar>
      </AppBar>

      <List>
        <ListItem button key="Earn" href="/earn" onClick={handleGoToEarnPage}>
          <ListItemIcon>
            <MonetizationOnOutlinedIcon />
          </ListItemIcon>
          <ListItemText primary="Earn" />
        </ListItem>

        <ListItem
          button
          key="Withdraw"
          href="/withdraw"
          onClick={handleGoToWithdrawPage}
        >
          <ListItemIcon>
            <AccountBalanceWalletRoundedIcon />
          </ListItemIcon>
          <ListItemText primary="Withdraw" />
        </ListItem>

        <Divider variant="middle" />
        {!authUser && (
          <List>
            <ListItem
              button
              key="Sign In"
              href="/signin"
              onClick={handleGoToSignInPage}
            >
              <ListItemIcon>
                <VpnKeyRoundedIcon />
              </ListItemIcon>
              <ListItemText primary="Sign In" />
            </ListItem>
          </List>
        )}

        {authUser && (
          <List>
            <ListItem
              button
              key="Profile"
              href="/profile"
              onClick={handleGoToProfilePage}
            >
              <ListItemIcon>
                <AccountBoxRoundedIcon />
              </ListItemIcon>
              <ListItemText primary="Profile" />
            </ListItem>

            <ListItem button key="Sign Out" onClick={handleSignOut}>
              <ListItemIcon>
                <ExitToAppRoundedIcon />
              </ListItemIcon>
              <ListItemText primary="Sign Out" />
            </ListItem>
          </List>
        )}
      </List>
    </>
  );

  return (
    <div className={classes.root}>
      <AppBar position="fixed" className={classes.appBar}>
        <Toolbar>
          <IconButton
            color="inherit"
            aria-label="open drawer"
            edge="start"
            onClick={handleDrawerToggle}
            className={classes.menuButton}
          >
            <MenuIcon />
          </IconButton>
          <div className={classes.rightAlign}>
            {authUser && <ProfileIcon className={classes.hoverCursor} />}
            {!authUser && (
              <Button color="inherit" onClick={handleGoToSignInPage}>
                Sign In
              </Button>
            )}
          </div>
        </Toolbar>
      </AppBar>

      <nav className={classes.drawer} aria-label="sidebar">
        <Hidden mdUp>
          <Drawer
            variant="temporary"
            anchor={classes.direction === "rtl" ? "right" : "left"}
            open={mobileOpen}
            onClose={handleDrawerToggle}
            classes={{
              paper: classes.drawerPaper
            }}
            ModalProps={{
              keepMounted: true // Better open performance on mobile.
            }}
          >
            {drawer}
          </Drawer>
        </Hidden>
        <Hidden smDown>
          <Drawer
            classes={{
              paper: classes.drawerPaper
            }}
            variant="permanent"
            open
          >
            {drawer}
          </Drawer>
        </Hidden>
      </nav>

      <main className={classes.content}>
        <div className={classes.toolbar} />
        {children}
      </main>
    </div>
  );
};

const mapStateToProps = state => ({
  authUser: state.sessionState.authUser
});

export default connect(mapStateToProps)(Shell);

Как видите, компонент Shell использует HO C, чтобы обернуть его реквизитом authUser из состояния сеанса. , Я не знаю, является ли это причиной проблем при загрузке страницы?

Компонент ProfileIcon не загружается сразу, когда пользователь входит в систему. Аналогично сайту kage, о котором я упоминал ранее. Я не понимаю, почему это происходит. Я чувствую, что мой код повсюду.

Моя signin.js страница выглядит так:

import React from "react";
import Router from "next/router";

import Button from "@material-ui/core/Button";
import { AppWithAuthentication } from "../src/components/App";
import { auth, provider } from "../src/firebase/firebase";
import { db } from "../src/firebase";
import * as routes from "../src/constants/routes";

const SignInPage = () => (
  <AppWithAuthentication>
    <h1>Sign In</h1>
    <SignInForm />
  </AppWithAuthentication>
);

const updateByPropertyName = (propertyName, value) => () => ({
  [propertyName]: value
});

const INITIAL_STATE = {
  user: null,
  error: null
};

class SignInForm extends React.Component {
  constructor(props) {
    super(props);

    this.state = { ...INITIAL_STATE };

    if (auth.currentUser) {
      console.log(`already signed in`);
      Router.push(routes.HOME);
    }
  }

  componentDidMount() {
    auth.onAuthStateChanged(user => {
      if (user) {
        console.log(user);

        // add them to the db and then redirect
        db.doCreateUser(
          user.uid,
          user.email,
          user.displayName,
          user.photoURL,
          false
        )
          .then(() => {
            this.setState(() => ({ ...INITIAL_STATE }));
            Router.push(routes.HOME);
          })
          .catch(error => {
            this.setState(updateByPropertyName("error", error));
          });
      } else {
        console.log(`No active user found. User must log in`);
      }
    });
  }

  onClick = () => {
    auth.signInWithRedirect(provider);
  };

  render() {
    return (
      <Button variant="contained" color="primary" onClick={this.onClick}>
        Sign In with Google
      </Button>
    );
  }
}

export default SignInPage;

export { SignInForm };

Где AppWithAuthentication выглядит так:

import React from "react";
import { compose } from "recompose";

import withAuthentication from "../Session/withAuthentication";
import withAuthorisation from "../Session/withAuthorisation";

const App = ({ children }) => (
  <div className="app">
    {children}
  </div>
);

const AppWithAuthentication = compose(
  withAuthentication,
  withAuthorisation(false)
)(App);

const AppWithAuthorisation = compose(
  withAuthentication,
  withAuthorisation(true)
)(App);

export { AppWithAuthentication, AppWithAuthorisation };

Поэтому, когда пользователь заходит на мою веб-страницу и пытается получить доступ к любому маршруту «только аутентифицированный», он сначала видит содержимое маршрута в течение нескольких секунд, , а затем перенаправляются на страницу входа. Я не хочу, чтобы это произошло, и я не могу понять, почему это происходит.

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

Ответы [ 2 ]

0 голосов
/ 19 января 2020

Удалить эту часть

static async getInitialProps({ Component, ctx }) {
    return {
      pageProps: Component.getInitialProps
        ? await Component.getInitialProps(ctx)
        : {}
    };
  }

Пусть затем js самостоятельно выполнит оптимизацию. когда вы сами обрабатываете начальный реквизит, оптимизация теряется.

0 голосов
/ 18 января 2020

Это вызвано запросом процесса аутентификации, ожидающим ответа. Проверьте сетевые подключения в chrome с помощью инструментов разработчика (Ctrl + Shift + I) -> Сеть -> Затем перезагрузите приложение и увидите, как выполняются запросы. Вы заметите, что ваш штат будет ждать https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyAssertion?key=xxx, чтобы вернуть ответ с именем учетной записи Google et c. после того, как вы нажмете «Войти» и выберите учетную запись.

Установите видимость загрузчиков в зависимости от информации, которую возвращает запрос (создайте переменную в вашем штате (например, пользователь), и как только у вас будет информация о пользователе из набора Google это к объекту пользователя. Затем вы можете сказать! user -> show loader. Вы также можете запустить загрузчик с помощью дополнительной переменной состояния, которая устанавливается в true при нажатии входа.

Учитывая, что вы используете firebase обратитесь к https://www.robinwieruch.de/complete-firebase-authentication-react-tutorial для получения дополнительной информации. Я бы также включил сюда код, но я думаю, что вы можете использовать эту информацию для построения того, что я сказал, в соответствии с тем, как вы хотите структурировать свое приложение. не слишком большая сделка. Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...