【问题标题】:NextJS Router.push not working for certain use casesNextJS Router.push 不适用于某些用例
【发布时间】:2021-05-26 02:18:30
【问题描述】:

我正在努力查明我的代码存在什么问题。基本上,我设置了一个 hooks.js 文件,如果用户通过身份验证,则检查并获取当前用户详细信息,否则返回 null。这个钩子应该是一个辅助函数,然后控制对页面的访问(即尝试访问登录/注册页面的经过身份验证的用户将被重定向到仪表板,反之亦然)。

但是,它目前只能部分工作。例如,如果我登录然后尝试访问登录/注册页面,它会自动将我重定向到仪表板页面。但是,如果我已注销并尝试访问仪表板,我可以访问它,但它不会将我重定向到登录页面。有谁知道我做错了什么?我试图通过遵循代码流(控制台在各个地方登录)来调试它,甚至在 hooks.js 中,它似乎 应该 已经执行了 Router.push(redirectTo) 代码但它似乎并没有这样做。任何帮助表示赞赏(以及下面包含的代码的 sn-ps)!

hooks.js

import { useEffect } from 'react';
import Router from 'next/router';
import useSWR from 'swr';

const fetcher = (url) =>
    fetch(url)
        .then((r) => r.json())
        .then((data) => {
            if (data?.user) {
                return { user: data?.user };
            } else {
                return null;
            }
        });

export function useUser({ redirectTo, redirectIfFound } = {}) {
    const { data, error } = useSWR('api/users/profile', fetcher);
    const user = data?.user;
    const finished = Boolean(data);
    const hasUser = Boolean(user);

    useEffect(() => {
        if (!redirectTo || !finished) return;

        if (
            // If redirectTo is set, redirect if the user was not found.
            (Boolean(redirectTo) && !redirectIfFound && !hasUser) ||
            // If redirectIfFound is also set, redirect if the user was found
            (redirectIfFound && hasUser)
        ) {
            Router.push(redirectTo);
        }
    }, [redirectTo, redirectIfFound, finished, hasUser]);

    return error ? null : user;
}

dashboard.js

import React from 'react';
import PrivateRouteLayout from '../components/privateRouteLayouts';
import { useUser } from '../lib/hooks';

function Dashboard() {
    useUser({ redirectTo: '/signin', redirectIfFound: false });

    return (
        <PrivateRouteLayout title='Dashboard'>
            <div>
                <h1>Welcome !</h1>
            </div>
        </PrivateRouteLayout>
    );
}

export default Dashboard;

signinform.js

import React, { useState, useEffect } from 'react';
import Link from 'next/link';
import Router from 'next/router';
import { useUser } from '../lib/hooks';
import validateSigninInput from '../lib/validation/login';

function SigninForm() {
    useUser({ redirectTo: '/dashboard', redirectIfFound: true });
    const [userDetails, setUserDetails] = useState({
        email: '',
        password: '',
    });
    const [error, setError] = useState({
        email: '',
        password: '',
    });

    function handleChange(event) {
        setError({
            email: '',
            password: '',
        });
        setUserDetails({
            ...userDetails,
            [event.target.name]: event.target.value,
        });
    }

    async function handleSubmit(event) {
        event.preventDefault();

        const body = userDetails;

        try {
            const res = await fetch('/api/users/signin', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(body),
            });

            if (res.status === 200) {
                Router.push('/dashboard');
            } else if (res.status === 400) {
                res.json().then((errMessage) => {
                    setError(errMessage);
                });
            }
        } catch (error) {
            console.error(error);
        }
    }

    return (
        <div className='valign-wrapper row auth-form-container'>
            <div className='row'>
                <div className='col s8 offset-s2'>
                    <Link href='/'>
                        <a>{'< Back to home page'}</a>
                    </Link>
                </div>
                <div className='input-field col s8 offset-s2'>
                    <label htmlFor='email'>Email</label>
                    <input
                        type='email'
                        onChange={handleChange}
                        name='email'
                        value={userDetails.email}
                    />
                    <p className='error-message'> {error.email} </p>
                </div>
                <div className='input-field col s8 offset-s2'>
                    <label htmlFor='password'>Password</label>
                    <input
                        type='password'
                        onChange={handleChange}
                        name='password'
                        value={userDetails.password}
                    />
                    <p className='error-message'>{error.password}</p>
                </div>
                <div className='col s8 offset-s2'>
                    <button
                        className='btn waves-effect waves-light submit-button'
                        onClick={handleSubmit}
                    >
                        Login
                    </button>
                </div>
                <div className='col s8 offset-s2 mt-10'>
                    <Link href='/register'>
                        <a>Don't have an account yet? Create one here</a>
                    </Link>
                </div>
            </div>
        </div>
    );
}

export default SigninForm;

【问题讨论】:

    标签: reactjs next.js


    【解决方案1】:

    为此,您必须从服务器端和客户端检查用户是否已通过身份验证以获取受保护的路由。将authInitialProps 用于受保护的页面。

    dashboard.js

    import React from 'react';
    import PrivateRouteLayout from '../components/privateRouteLayouts';
    import { useUser } from '../lib/hooks';
    import { authInitialProps } from './auth';
    
    function Dashboard() {
        useUser({ redirectTo: '/signin', redirectIfFound: false });
    
        return (
            <PrivateRouteLayout title='Dashboard'>
                <div>
                    <h1>Welcome !</h1>
                </div>
            </PrivateRouteLayout>
        );
    }
    
    Dasboard.getInitialProps = authInitialProps(true);
    
    export default Dashboard;
    

    auth.js

    const WINDOW_USER_SCRIPT_VAR = '__USER__';
    
    // Server side function
    export const getServerSideToken = (req) => {
        const { signedCookies = {} } = req;
    
        if (!signedCookies) {
            return {};
        } else if (!signedCookies.cookie) {
            return {};
        }
    
        return { user: signedCookies.cookie };
    };
    
    
    export const getClientSideToken = () => {
        if (typeof window !== 'undefined') {
            const user = window[WINDOW_USER_SCRIPT_VAR] || {};
            return { user };
        }
        return { user: {} };
    };
    
    /**
     * Check for authenticated users
     * @param {Boolean} isProtectedRoute 
     */
    
    export const authInitialProps = (isProtectedRoute) => ({ req, res }) => {
        const auth = req ? getServerSideToken(req) : getClientSideToken();
        const currentPath = req ? req.url : window.location.pathname;
        const user = auth.user;
        const isAnonymous = !user || user.type !== 'authenticated';
        if (isProtectedRoute && isAnonymous && currentPath !== '/login') {
            return redirectUser(res, `/login?redirect=${currentPath}`, currentPath);
        }
    
        return { auth };
    };
    

    【讨论】:

    • 嗨 Zain,谢谢。我还没有机会尝试这个,但你能解释一下为什么我需要添加 auth.js 吗? useSWR 挂钩已经调用了检查用户会话的 api/users/profile 页面,所以我不太明白为什么仍然需要 auth.js 文件。提前致谢!
    • @KSoenandar 这只是一个参考,你可以在你自己的文件中添加这些帮助函数...... authInitialProps 的目的是从客户端和服务器端检查cookie或令牌是否存在......如果没有,它会将用户重定向到登录页面。
    猜你喜欢
    • 1970-01-01
    • 2014-12-17
    • 2013-06-26
    • 2021-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多