У меня есть компонент/форма <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
});
У меня кодыпесочница здесь.
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,
handleChangeEvent,
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>
);



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Поскольку вы используете React Router, я бы рекомендовал следовать примеру в их документах для поток аутентификации и создать компонент 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. Вероятно, вы также захотите реализовать способ взаимодействия с контекстом, поскольку он должен быть динамическим. На основе Реагировать на документацию это может выглядеть примерно так:
// 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>
);
}
В вашем образце есть некоторые ошибки, но временная работа над ними будет выглядеть так: codeandbox.io/s/logincomponent-rq44y
Потрясающий. Это проясняет мое замешательство. И последний вопрос: если я хочу изменить способ отображения входа/выхода в строке меню, что мне нужно передать этому компоненту меню? Например, если администратор аутентифицирован и вошел в систему, отображать только кнопку «Выход»?
Так же, как и для пользовательского компонента маршрута, вы должны использовать AuthContext для проверки подлинности пользователя. Базовый образец здесь: codeandbox.io/s/logincomponent-le4cm
передача функции logout из Auth, кажется, не очищает isAuthenticated. Я верю, что смогу понять это. В любом случае, спасибо за помощь, я очень ценю это.
Не могли бы вы предоставить рабочий пример, разветвив коды и ящик, которые я предоставил?