首页 > 解决方案 > 使用带有 React 的 Context API 的 react-router-dom 的意外输出

问题描述

对于我的一个小项目,我正在尝试使用没有 Redux 的 React 上下文 API 来实现最基本的身份验证。

import { createContext, useContext, useState } from 'react'

export const AuthContext = createContext()

export const useAuth = () => {
    const context = useContext(AuthContext)

    if(context === null) throw new Error('Nope')

    return context  
}

export const AuthProvider = (props) => {
    const [authenticated, setAuthenticated] = useState(false)

    const login = () => {
        setAuthenticated(true)

        localStorage.setItem(storageKey, true)
    }
    
    const logout = () => {
        setAuthenticated(false)

        localStorage.setItem(storageKey, false)
    }
    
    return <AuthContext.Provider value={{authenticated, login, logout}} {...props}/>
}

export default AuthContext

我创建了一个上下文,并<App />像这样将我的组件包装在其中;<AuthProvider></App></AuthProvider>. 因为我想保持经过身份验证的状态,所以我使用浏览器的本地存储来存储一个简单的布尔值。

import PrivateRoute from './PrivateRoute'
import { useAuth } from './context/AuthContext'

import { AuthPage } from './pages'

import {
    BrowserRouter,
    Switch,
    Route,
} from 'react-router-dom'

import { useEffect } from 'react'

const App = () => {
    const { login, authenticated } = useAuth()

    useEffect(() => {
        const token = localStorage.getItem('user')

        if(token && token !== false) { login() }
    })

    return (
        <BrowserRouter>
            <Switch>
                <PrivateRoute exact path="/auth" component={AuthPage} />
                <Route exact path='/'>
                    Dashboard
                </Route>
            </Switch>
        </BrowserRouter>
    )
}

export default App

然后,在我的<App />组件中,我尝试调用login从 给出的回调,AuthProvider这让我假设这让我在页面刷新期间登录。当我尝试访问authenticated当前组件中的变量时,它确实有效。它表明我已通过身份验证。

但是,当我尝试设置 PrivateRoute 时,只有经过身份验证的用户才能这样做:

import {
    Route,
    Redirect
} from 'react-router-dom'

import { useAuth } from './context/AuthContext'

const PrivateRoute = ({ component: Component, ...rest }) => {
    const { authenticated } = useAuth()
    
    if(authenticated) {
        return <Route {...rest} render={(props) => <Component {...props} />} />
    }

    return <Redirect to={{ pathname: '/login' }} />
}

export default PrivateRoute

这没用。它只是将我重定向到登录页面。这是怎么来的?PrivateRoute组件正在从组件中呈现<App />。另外,这个问题的解决方案是什么?

标签: javascriptreactjsreact-router-domreact-context

解决方案


与其在每次重新渲染时都运行一个useEffect来检查用户是否应该登录,不如authenticated使用 localStorage 中的值来初始化你的状态:

const storageKey = 'user'
const initialState = JSON.parse(localStorage.getItem(storageKey)) ?? false

export const AuthProvider = (props) => {
  const [authenticated, setAuthenticated] = useState(initialState)

  const login = () => {
      setAuthenticated(true)

      localStorage.setItem(storageKey, true)
  }
  
  const logout = () => {
      setAuthenticated(false)

      localStorage.setItem(storageKey, false)
  }
  
  return <AuthContext.Provider value={{authenticated, login, logout}} {...props}/>
}

推荐阅读