使用私有路由和上下文进行身份验证
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()
使其不提交表单。
我正在尝试使用私有路由和上下文向 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()
使其不提交表单。