Справочная информация
Я только начал использовать / изучаю, как использовать 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 };
Поэтому, когда пользователь заходит на мою веб-страницу и пытается получить доступ к любому маршруту «только аутентифицированный», он сначала видит содержимое маршрута в течение нескольких секунд, , а затем перенаправляются на страницу входа. Я не хочу, чтобы это произошло, и я не могу понять, почему это происходит.
Как я могу исправить эти проблемы? Я полностью застрял на идеях. Нужна пара глаз, чтобы помочь мне понять, в чем проблема.