如何用React/Redux/Formik和Yup构建我的高阶组件



我有两个独立的视图。它们都使用相同的形式。形式生活在模态中。该表单在第一个视图中完美工作(创建模式)

现在我想为(编辑模式)添加另一个。唯一的区别是编辑模式需要实际项目的值(在我们的情况下是用户)

一般来说,我了解高阶组件的工作原理。但我目前的配置是使用Redux, Formik and Yup分别获取数据、表单处理和验证。

我在网上试了几个例子,但没有一个符合我的情况。问题就在这里。我应该向高阶组件转移多少功能?

功能一:实际形式应该存在的地方。组件,还是HOC?Code Below:

我应该在HOC中移动它吗?既然是完全相同的形式。如果是,为什么?

功能2:我正在和Yup一起做表单验证。我已经将此功能移到了HOC中,但现在,我无法将ValidationSchema传递给表单组件。Code Below:

功能3:如何处理模态。莫代尔有点棘手。我决定在每个单独的屏幕上单独使用它。这是正确的吗?还是我应该把它包括在HOC中?

我的HOC:

import React, { Component } from 'react';
import { withFormik } from 'formik';
import PropTypes from 'prop-types';
import * as Yup from 'yup';
import { createUser, updateUser } from './service';
import { listGroups } from '../groups/service';
const AddEditUserHOCForm = WrappedComponent => {
class ViewUser extends Component {
static propTypes = {
user: PropTypes.object,
onCancel: PropTypes.func,
onSave: PropTypes.func,
status: PropTypes.string,
values: PropTypes.object,
errors: PropTypes.object,
isSubmitting: PropTypes.bool,
handleChange: PropTypes.func,
handleSubmit: PropTypes.func,
setStatus: PropTypes.func
};
static defaultProps = {
user: {},
onCancel: () => { },
onSave: () => { },
status: '',
values: {},
errors: {},
isSubmitting: false,
handleChange: () => { },
handleSubmit: () => { },
setStatus: () => { }
};
state = {
groups: [],
isRetrievingData: false
};
componentDidMount() {
this.setState({
isRetrievingData: true
});
return new Promise([listGroups()])
.then(values => values.map(res => res.data))
.then(([groups]) => {
this.setState({
isRetrievingData: false,
groups
});
})
.catch(({ message = 'Could not retrieve data from server.' }) => {
this.setState({
isRetrievingData: false
});
this.props.setStatus(message);
});
}
handleCancel = () => {
const { onCancel } = this.props;
if (onCancel) {
onCancel();
}
};
render() {
return (
<WrappedComponent
{...this.props}
{...this.state}
onSubmit={this.handleSubmit}
onCancel={this.handleCanel}
/>
);
}
}
const UserValidationSchema = Yup.object().shape({
username: Yup.string('Provide a Username').required('Username is Required'),
password: Yup.string().email('Provide a Valid email Address'),
confirmPassword: Yup.string('Enter your password again')
.required('Password Confirmation is Required')
.oneOf([Yup.ref('password')], 'Passwords do not match')
});
const NewUserFormHOC = withFormik({
mapPropsToValues: ({ user }) => ({ ...user }),
UserValidationSchema,
handleSubmit: (values, { props, setSubmitting, setStatus }) => {
const saveUser = values.username ? updateUser : createUser;
return saveUser(values)
.then(() => {
setSubmitting(false);
setStatus('');
props.onSave(values);
props.onCancel();
})
.catch(({ message, response: { data } }) => {
setSubmitting(false);
setStatus(data || message);
});
},
displayName: 'ViewUser'
})(ViewUser);
return NewUserFormHOC;
};
export default AddEditUserHOCForm;

这是我的表单组件:

import React, { Component, Fragment } from 'react';
import { Formik, Form, Field } from 'formik';
import PropTypes from 'prop-types';
import AddEditUserHOCForm from './components/AddEditUserHOC'
import LensesSelect from './data/ReactSelectComponent';
import formPropTypes from '../constants/formPropTypes';
import { listGroups } from '../groups/service';
class UserCreateForm extends Component {
static propTypes = {
...formPropTypes,
username: PropTypes.string,
email: PropTypes.string,
password: PropTypes.string,
confirmPassword: PropTypes.string,
groupSelect: PropTypes.func
};
static defaultProps = {
email: ''
};
state = {
type: 'password',
groups: []
};
componentDidMount() {
this.fetchListGroups();
}
fetchListGroups = () => {
listGroups().then(({ data }) => {
this.setState({ groups: data });
});
};
mapListGroupToSelect = () => {
const { groups } = this.state;
return groups.map(group => ({
label: group.name,
value: group.name
}));
};
togglePasswordMask = e => {
const { type } = this.state;
e.preventDefault();
this.setState(prevState => ({
passwordIsMasked: !prevState.passwordIsMasked,
type: type === 'password' ? 'input' : 'password'
}));
};
selectOnChangeCallback = response => {
// eslint-disable-next-line no-console
console.log('selectOnChangeCallback', response);
};
render() {
const { type } = this.state;
const selectData = this.mapListGroupToSelect();
return (
<Fragment>
<Formik
initialValues={{
username: '',
email: '',
password: '',
confirmPassword: ''
}}
// validationSchema={createUserValidationSchema}
onSubmit={values => {
// same shape as initial values
console.log(values);
}}
>
{({ errors, touched }) => (
<Form>
<div className="my-3">
<label>
Username <span className="text-danger">*</span>
</label>
<Field name="username" type="text" className="form-control rounded-0" />
{errors.username && touched.username ? (
<div className="text-danger">{errors.username}</div>
) : null}
</div>
<div className="my-3">
<label>email</label>
<Field name="email" type="email" className="form-control rounded-0" />
{errors.email && touched.email ? (
<div className="text-danger">{errors.email}</div>
) : null}
</div>
<div className="my-3">
<label>
Password <span className="text-danger">*</span>
</label>
<div className="d-flex align-items-center">
<Field type={type} name="password" className="form-control rounded-0 mr-2" />
<span
className={type === 'password' ? 'fa fa-eye fa-lg' : 'fa fa-eye-slash fa-lg'}
onClick={this.togglePasswordMask}
/>
</div>
{errors.password && touched.password ? (
<div className="text-danger">{errors.password}</div>
) : null}
</div>
<div className="my-3">
<label>
Confirm Password <span className="text-danger">*</span>
</label>
<Field name="confirmPassword" type={type} className="form-control rounded-0" />
{errors.confirmPassword && touched.confirmPassword ? (
<div className="text-danger">{errors.confirmPassword}</div>
) : null}
</div>
<div className="my-3">
<label>
Select Group <span className="text-danger">*</span>
</label>
<ReactSelectComponent
isMulti={false}
options={selectData}
onChangeCallback={this.selectOnChangeCallback}
/>
</div>
<button type="submit" className="btn btn-primary rounded-0 float-right my-5">
<span className="mx-2">Create User</span>
</button>
</Form>
)}
</Formik>
</Fragment>
);
}
}
export default AddEditUserHOCForm(UserCreateForm);

我知道这是一个有很多代码要纯通过。但是我不知所措。真的不知道我应该在哪里包括什么。

对我来说,我需要表格(Formik)和Yup,以及HOC上的Redux。然后根据视图添加数据。我真的需要一些指导和例子。非常感谢。

React很棒,但它给了你太多的自由。在我看来,您必须将SOLID原则应用于您的组件。

正如你可以通过网络阅读一样,你的组件应该只做一件事并做好它。

所以。如果我要这样做,我会把表单放在一个文件夹中,其中一个文件代表它的表示配置,另一个文件则代表业务逻辑。您可以将第二部分作为一个HOC。

比方说,UserForm.js,带有UserValidations,带有UserActions。然后把整个包裹起来。

然后你只需要在你想要的地方使用它。我希望它能有所帮助。

最新更新