更新反应上下文而不刷新状态更新页面

问题描述 投票:0回答:1

我目前正在使用以下堆栈构建应用程序: Next.js Node.js、Express.js PostgreSQL。

我使用 JWT HTTPOnly cookie 构建身份验证。下面是我如何登录、注销和验证用户是否登录的代码。

在前端,我在 nextjs 中使用 RTK 查询来处理 api 查询。

我创建了一个 SessionProvider 组件并包裹在布局文件中的子组件,因此当用户首次加载页面时,该组件会检查用户是否已登录,如果是,则会更新反应上下文。

"use client"
import { UserI } from '@/interfaces';
import { useVerifyQuery } from '@/lib/features/authApi';
import { createContext, PropsWithChildren } from 'react';

interface ContextI {
    user: UserI | null;
    isAuthenticated: boolean;
    error: string | null;
    isLoading: boolean;
}

export const SessionContext = createContext<ContextI>({
    user: null,
    isAuthenticated: false,
    error: null,
    isLoading: false
});

export const SessionProvider = ({ children }: PropsWithChildren) => {
    const { data, isLoading } = useVerifyQuery();

    const user = data?.data;
    const error = data?.error;

    if (error) console.log(error);

    return (
        <SessionContext.Provider value={{ user: user ?? null, isAuthenticated: user ? true : false, error: error ?? null, isLoading }}>
            {children}
        </SessionContext.Provider>
    );
};

我按以下方式使用反应上下文来检查用户是否经过身份验证,如果是,我以类似的方式更新导航链接

"use client"
import { SessionContext } from '@/app/SessionProvider';
import { ButtonGroup, Flex, HStack, Spinner } from '@chakra-ui/react';
import { usePathname } from 'next/navigation';
import { useContext } from 'react';
import LinkBtn from './LinkBtn';
import Logout from './Logout';

const NavLinks = () => {
    const { user, isAuthenticated, isLoading } = useContext(SessionContext);
    const currPath = usePathname();

    if (isLoading) return <Spinner />

    return (
        <Flex className='gap-3 justify-center items-center'>
            {isAuthenticated ?
                <>
                    <HStack>
                        <LinkBtn href={currPath === '/' ? '/dashboard' : '/'}>
                            {currPath === '/' ? 'Dashboard' : 'Home'}
                        </LinkBtn>
                        <Logout name={user?.name} />
                    </HStack>
                </>
                :
                <ButtonGroup>
                    <LinkBtn href='/user/signup'>Sign up</LinkBtn>
                    <LinkBtn href='/user/login'>Login</LinkBtn>
                </ButtonGroup>
            }
        </Flex>
    )
}

export default NavLinks

下面是我的登录组件

"use client"
import { handleErrors } from '@/components/error/handleErrors'
import { toast } from "@/components/error/Toast"
import Auth from '@/components/ui/Auth'
import Btn from '@/components/ui/Btn'
import ErrorMsg from '@/components/ui/ErrorMsg'
import LinkBtn from '@/components/ui/LinkBtn'
import Logo from '@/components/ui/Logo'
import { unexpectedError } from '@/constants'
import { AuthI } from '@/interfaces'
import { useLoginMutation } from '@/lib/features/authApi'
import { validateLogin } from '@/validation'
import { ButtonGroup, Input, Spinner } from '@chakra-ui/react'
import { zodResolver } from '@hookform/resolvers/zod'
import { useRouter } from 'next/navigation'
import { useForm } from "react-hook-form"

const Login = () => {
    const { register, handleSubmit, formState: { errors } } = useForm<AuthI>({
        resolver: zodResolver(validateLogin)
    });

    const navigation = useRouter();

    const [login, { isLoading, error }] = useLoginMutation();
    if (error) handleErrors(error, unexpectedError.type);

    const onSubmit = async (data: AuthI) => {
        try {
            await login(data);
            navigation.push("/");
        } catch (error) {
            toast.error(unexpectedError.message, { toastId: unexpectedError.type });
            console.log(error);
        }
    };

    return (
        <Auth>
            <form className='flex flex-col gap-2 p-10' onSubmit={handleSubmit(onSubmit)}>
                <Logo />
                {errors && <ErrorMsg>{errors.email?.message}</ErrorMsg>}
                <Input type='email' isRequired placeholder='Enter your email...' {...register("email")} />
                {errors && <ErrorMsg>{errors.password?.message}</ErrorMsg>}
                <Input type='password' isRequired placeholder='Enter your password...' {...register("password")} />
                <ButtonGroup my={2}>
                    <Btn type='submit' isDisabled={isLoading}>
                        Login {isLoading && <Spinner ml={1} size='sm' />}
                    </Btn>
                    <LinkBtn href='/'>
                        Cancel
                    </LinkBtn>
                </ButtonGroup>
            </form>
        </Auth>
    )
}

export default Login

我面临的问题是,每当我登录或注销时,反应上下文都不会更新状态,直到我刷新页面为止,如何确保状态已更新,更改已推送。我对如何修复它完全一片空白。请帮忙!

感谢您对此进行调查。

我尝试寻找不同的方法来解决这个问题,例如 useEffect、useState,我尝试将 React 上下文更改为 redux 切片,但我找不到解决此问题的方法。

node.js authentication next.js react-context cookie-httponly
1个回答
0
投票

感谢大家对此进行调查。我终于找到了解决方案。这是代码。我只需要使用钩子。

"use client"
import { ContextI } from '@/interfaces';
import { useVerifyQuery } from '@/lib/features/authApi';
import { createContext, PropsWithChildren, useEffect, useState } from 'react';

const initialContext = {
    user: null,
    isAuthenticated: false,
    error: null,
    isLoading: false,
} as ContextI;

export const SessionContext = createContext<ContextI>(initialContext);

export const SessionProvider = ({ children }: PropsWithChildren) => {
    const { data, isLoading } = useVerifyQuery();

    const user = data?.data ?? null;
    const error = data?.error ?? null;

    const [context, setContext] = useState<ContextI>({
        user,
        isAuthenticated: !!data?.data,
        error,
        isLoading,
        updateContext: (newContext: Partial<ContextI>) => {
            setContext((prevContext) => ({
                ...prevContext,
                ...newContext,
            }));
        }
    });

    useEffect(() => {
        setContext((prevContext) => ({
            ...prevContext,
            user,
            isAuthenticated: !!user,
            error,
            isLoading
        }));
    }, [data, isLoading]);

    return (
        <SessionContext.Provider value={{ ...context }}>
            {children}
        </SessionContext.Provider>
    );
};
© www.soinside.com 2019 - 2024. All rights reserved.