向上下文提供者提供formik表单值

时间:2019-07-12 16:51:14

标签: javascript reactjs

我有一个<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>
);

1 个答案:

答案 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>
  );
}