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

时间:2019-10-25 17:04:38

标签: reactjs typescript react-router react-context

我正在尝试使用私有路由和上下文向React + Typescript应用添加简单身份验证。我有一个带有按钮的简单登录组件,该组件仅将上下文中的布尔值var authenticated设置为true。专用路由应检查此变量,如果不正确,则重定向到登录组件,否则显示指定的组件。问题是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,则会看到相反的行为,就永远不会看到登录页面。这应该是动态的吗?

2 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:0)

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

之所以这样,是因为在我的登录组件中,我在表单中有一个按钮,该按钮会自动提交表单并重新加载页面。

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