[英]How to structure my Higher Order Component with React/Redux/Formik and Yup?
我有2個獨立的視圖。 他們兩個都使用相同的形式。 形式生活在模態中。 該表單在第一個視圖(“創建模式”)中可以完美運行。
現在,我想為(編輯模式)添加另一個。 唯一的區別是編輯模式需要實際Item的值(在本例中為用戶) 。
我通常了解高階組件的工作方式。 但是我當前的配置是使用Redux, Formik and Yup
分別獲取數據,表單處理和驗證。
我在網上嘗試了一些示例,但沒有一個適合我的情況。 問題就在這里。 我應該將什么功能移至高階組件?
功能一:實際形式應居住的地方。 組件還是HOC? Code Below:
我應該在HOC中移動它嗎? 由於它是完全相同的形式。 如果是,為什么?
功能2:我正在使用Yup進行表單驗證。 我已經將此功能移到了HOC中,但是現在,我無法將ValidatioSchema傳遞給表單組件。 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,withUserValidations,withUserActions。 然后包裹整個東西。
然后,您只需要在需要的地方使用它即可。 希望對您有所幫助。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.