我有一个<Login>
组件/表单,该组件/表单使用<Formik>
来跟踪表单状态。我想将一个特定的值props.values.userType
传递给上下文提供者,以便将该属性传递给我的路由组件。
我的目标是重定向未以管理员身份登录的用户,如果他们确实是管理员,则继续按正常方式呈现路由。
所以我创建了一个AuthContext
。
const AuthContext = React.createContext();
在我的<Login>
组件中,下面有<Formik>
组件。我应该在哪里使用AuthContext.Provider
,如何将values.props.userType
传递给该提供者? props.values.userType
是否应在该<Formik>
组件所在的类组件的状态下初始化?
还是应该以跟踪userType
的状态创建对象存储?像这样
export const AuthContext = createContext({
user: null,
isAuthenticated: null
});
我有一个codesandbox here。
class FormikLoginForm extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
const {} = this.state;
return (
<Formik
initialValues={{
username: "",
password: "",
userType: "",
}}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 500);
}}
validationSchema={Yup.object().shape({
userType: Yup.string().required("User type is required"),
username: Yup.string().required(
"Required -- select a user type role"
),
password: Yup.string().required("Password is required"),
})}
>
{props => {
const {
values,
touched,
errors,
dirty,
isSubmitting,
handleChange,
handleBlur,
handleSubmit,
handleReset
} = props;
return (
<>
<Grid>
<Grid.Column>
<Header as="h2" color="teal" textAlign="center">
Log-in to your account
</Header>
<Form size="large" onSubmit={handleSubmit}>
<Segment stacked>
<RadioButtonGroup
id="userType"
label="User Type"
value={values.userType}
error={errors.userType}
touched={touched.userType}
>
然后在我的index.js
文件中,我在其中渲染了所有路线,我的AdminRoute
使用了我上面描述的逻辑
const AdminRoute = props => {
const { userType, ...routeProps } = props;
if (userType !== "admin") return <Redirect to="/login" />;
return <Route {...routeProps} />;
};
const Routes = () => (
<Router>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/login" component={FormikLoginForm} />
<Route exact path="/admin" component={AdminPage} />
/>
<Route path="/admin/change-password" component={ChangePassword} />
</Switch>
</Router>
);
答案 0 :(得分:1)
在使用React Router时,我建议遵循其文档中的示例authentication flow,并创建一个PrivateRoute
组件,并使用它代替常规的Route
组件。
Formik本身并不是解决方案,它只是一种使与表单进行交互和执行表单验证更加容易的方法。让用户传递自己的类型(该类型以后用于授权)似乎不是一个好主意,除非身份验证流程也需要这样做。
对于userType
,在我看来,应该通过API端点成功登录,或者从您使用的任何登录后端索取索赔。是的,您可以将其存储在AuthContext中并像这样使用它(假设您在项目设置中使用React 16.8 +):
function AdminPrivateRoute({ component: Component, ...rest }) {
const auth = React.useContext(AuthContext);
return (
<Route
{...rest}
render={props =>
auth.isAuthenticated && auth.userType === 'admin' ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
/>
);
}
AuthContext.Provider
组件应靠近组件树顶部(如果不在顶部)使用。在您的代码示例中,我会在Routes
组件中说。您可能还希望实现一种与上下文交互的方式,因为它需要动态。基于React documentation,它看起来可能像这样:
// may want to pass initial auth state as a prop
function AuthProvider({ children }) {
const [state, setState] = React.useState({
isAuthenticated: false,
userType: null,
// other data related to auth/user
});
// may or may not have use for React.useMemo here
const value = {
...state,
// login() does not validate user credentials
// it simply sets isAuthenticated and userType
login: (user) => setState({ isAuthenticated: true, userType: user.type }),
// logout() only clears isAuthenticated, will also need to clear auth cookies
logout: () => setState({ isAuthenticated: false, userType: null }),
};
return (
<AuthContext.Provider value={value}>{children}</AuthContext.Provider>
);
}