NextJS Router.push在某些用例中不起作用

时间:2020-07-20 10:31:37

标签: reactjs next.js

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

但是,它目前仅部分起作用。例如,如果我登录然后尝试访问登录/注册页面,它将自动将我重定向到仪表板页面。但是,如果我注销并尝试访问仪表板,则可以访问它,并且它不会将我重定向到登录页面。有人知道我在做什么错吗?我试图通过遵循代码流(在各个地方进行控制台日志记录)来调试它,甚至在hooks.js中,似乎应该已经执行了Router.push(redirectTo)代码。但它似乎没有做到。感谢您的任何帮助(以及下面的代码片段)!

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;

1 个答案:

答案 0 :(得分:0)

为此,您将必须从服务器端和客户端进行检查,以检查用户是否通过了受保护的路由的身份验证。将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 };
};