使用私有路由和上下文进行身份验证

React authentication with private routes and context

我正在尝试使用私有路由和上下文向 React+Typescript 应用程序添加简单的身份验证。我有一个简单的登录组件,带有一个按钮,该按钮仅将上下文中的布尔变量 authenticated 设置为 true。私有路由应该检查这个 var 并重定向到登录组件,如果它不是真的,否则显示指定的组件。问题是 authenticated 似乎总是错误的,我总是被重定向到登录页面。

当我调试它时,我可以看到单击登录按钮时调用 AuthContextProvider 中的 setAuthenticated 函数。但是如果我然后点击任何私人路线的链接 authenticated 总是错误的。

这是我的 App.tsx:

function App() {

  return (
    <AuthContextProvider>
      <Router>
        <Link to="/">Home</Link>
        <Link to="/projects">Projects</Link>
        <div>
          <Route path="/login" component={Login} />
          <PrivateRoute path="/" exact component={Home} />
          <PrivateRoute path="/projects" component={Projects} />
        </div>
      </Router>
    </AuthContextProvider>
  );
} 

export default App;

PrivateRoute.tsx:

interface PrivateRouteProps extends RouteProps {
    // tslint:disable-next-line:no-any
    component: any;
}

const PrivateRoute = (props: PrivateRouteProps) => {
    const { component: Component, ...rest } = props;

    return (
      <AuthContextConsumer>
        {authContext => authContext && (

        <Route {...rest}
            render={ props => 
                authContext.authenticated === true  ? (
                    <Component {...props} />
                ) : (
                    <Redirect to="/login" />
                )
            }
        />

      )}
      </AuthContextConsumer>
    );
};

export default PrivateRoute;

AuthContext.tsx:

export interface AuthContextInterface {
  authenticated: boolean,
  setAuthenticated(newAuthState: boolean):void
}

const ctxt = React.createContext<AuthContextInterface>({
    authenticated: false,
    setAuthenticated: () => {}
  });

export class AuthContextProvider extends React.Component {
  setAuthenticated = (newAuthState:boolean) => {
    this.setState({ authenticated: newAuthState });
  };

  state = {
    authenticated: false,
    setAuthenticated: this.setAuthenticated,
  };

  render() {
    return (
      <ctxt.Provider value={this.state}>
        {this.props.children}
      </ctxt.Provider>
    );
  }
}  
export const AuthContextConsumer = ctxt.Consumer;

Login.tsx:

function Login() {

    return (
        <AuthContextConsumer>
        {({ authenticated, setAuthenticated }) => (
               <div>
                    <p>Login</p>
                    <form>
                        <input type="text" placeholder="Username"/>
                       <input type="password" placeholder="Password"/>
                        <button onClick={event => {
              setAuthenticated(true);
            }}>Login</button>
                    </form>
                </div>
        )}
        </AuthContextConsumer>
    );
}

export default Login;

我怀疑是 AuthContextProvider 中的状态定义有问题。如果我将此处的 authenticated 更改为 true,我会看到相反的行为,我永远不会看到登录页面。这应该是动态的吗?

问题原来是每次按下登录按钮时应用程序都在重新加载,因此丢失了 AuthContext 中的状态。

原因是在我的登录组件中,我在表单中有一个按钮,它会自动提交表单并重新加载页面。

解决方法是删除表单标签,或者在按钮中指定属性 type="button"

或者,在 onClick 回调中,设置 event.preventDefault() 使其不提交表单。