Как структурировать мой компонент высшего порядка с помощью React / Redux / Formik и Yup? - PullRequest
0 голосов
/ 08 февраля 2019

У меня есть 2 отдельных вида.Оба они используют одну и ту же форму.Форма живет в модале.Форма отлично работает в первом представлении (Режим создания) .

Теперь я хочу добавить еще один, для (Режим редактирования) .Единственное отличие состоит в том, что в режиме редактирования нужны значения фактического элемента (в нашем случае пользователь) .

Я понимаю, как работают компоненты высшего порядка в целом.Но моя текущая конфигурация использует 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, а также Redux в HOC.А затем добавить данные в зависимости от представления.Пожалуйста, мне действительно нужны рекомендации и примеры.Спасибо.

1 Ответ

0 голосов
/ 08 февраля 2019

Реакция великолепна, но дает слишком много свободы.По моему мнению, к вашим компонентам должны применяться принципы SOLID.

Поскольку вы можете читать через Интернет, ваш компонент должен делать только одно и делать это хорошо.

Итак.Если бы я делал это, у меня была бы форма в одной папке с одним файлом, представляющим его конфигурацию представления, и другим с бизнес-логикой.Вы можете сделать вторую часть как один HOC.

Допустим, UserForm.js, с пользовательскими утверждениями, с пользовательскими действиями.Затем оберните все это.

Тогда вам просто нужно будет использовать его там, где вы хотите.Надеюсь, это поможет.

...