簡體   English   中英

如何使用React / Redux / Formik和Yup構建我的高階組件?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM