簡體   English   中英

重構幾乎相同的組件,formik反應

[英]Refactoring almost identical components, formik react

我有兩個相同的組件,只有幾個區別(一個)。 有很多重復的代碼和樣板,但是我不確定如何重構它,因此我大概只需要提供一個配置即可。

LoginPage.js

import React from 'react';
import { Link } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import { Formik, FastField, Form, ErrorMessage } from 'formik';

import PropTypes from 'prop-types';
import { FormDebug } from 'utils/FormDebug';
import { LoginValidationSchema } from 'validations/AuthValidationSchema';

function LoginPage({ username, onChangeUsername, onSubmitForm }) {
  return (
    <div>
      <Helmet>
        <title>Login</title>
      </Helmet>
      <Formik
        initialValues={{ username, password: '' }}
        validationSchema={LoginValidationSchema}
        onSubmit={onSubmitForm}
        render={({ isSubmitting, isValid, handleChange }) => (
          <Form>
            <FastField
              type="text"
              name="username"
              render={({ field }) => (
                <input
                  {...field}
                  onChange={e => {
                    handleChange(e);
                    onChangeUsername(e);
                  }}
                />
              )}
            />
            <ErrorMessage name="username" component="div" aria-live="polite" />
            <FastField type="password" name="password" />
            <ErrorMessage name="password" component="div" aria-live="polite" />
            <button type="submit" disabled={isSubmitting || !isValid}>
              Login
            </button>
            <FormDebug />
          </Form>
        )}
      />
      <Link to="/auth/forgot_password">Forgot Password</Link>
    </div>
  );
}

LoginPage.propTypes = {
  username: PropTypes.string,
  onSubmitForm: PropTypes.func.isRequired,
  onChangeUsername: PropTypes.func.isRequired,
};

export default LoginPage;

ForgotPasswordPage.js

import React from 'react';
import { Helmet } from 'react-helmet';
import { Formik, FastField, Form, ErrorMessage } from 'formik';

import PropTypes from 'prop-types';
import { FormDebug } from 'utils/FormDebug';
import { ForgotPasswordValidationSchema } from 'validations/AuthValidationSchema';

function ForgotPasswordPage({ username, onChangeUsername, onSubmitForm }) {
  return (
    <div>
      <Helmet>
        <title>Forgot Password</title>
      </Helmet>
      <Formik
        initialValues={{ username }}
        validationSchema={ForgotPasswordValidationSchema}
        onSubmit={onSubmitForm}
        render={({ isSubmitting, isValid, handleChange }) => (
          <Form>
            <FastField
              type="text"
              name="username"
              render={({ field }) => (
                <input
                  {...field}
                  onChange={e => {
                    handleChange(e);
                    onChangeUsername(e);
                  }}
                />
              )}
            />
            <ErrorMessage name="username" component="div" aria-live="polite" />
            <FormDebug />
            <button type="submit" disabled={isSubmitting || !isValid}>
              Reset Password
            </button>
          </Form>
        )}
      />
    </div>
  );
}

ForgotPasswordPage.propTypes = {
  username: PropTypes.string,
  onSubmitForm: PropTypes.func.isRequired,
  onChangeUsername: PropTypes.func.isRequired,
};

export default ForgotPasswordPage;

如果您是我,那么如何重構它。

我在想如何,但是我不確定如何稱呼通過“孩子”,這是不同的

抱歉,如果您沒有尋找一般性的答案,但是我認為您不會通過概括那些看似相關的組件來提高可維護性。 我希望這些組件在您的應用程序成熟時會進一步分離,例如,通過添加社交登錄名,“記住我”選項,驗證碼,通過電子郵件檢索用戶名和密碼的選項,檢索密碼與簽名時對未知用戶名的不同處理等等。此外,這是組件的一部分,您真的不想弄錯,所以請KISS和所有。 最后,考慮是否真的存在這種半通用的登錄或檢索密碼表單組件的第三個用例。

盡管如此,仍可以通過創建一個可重用的UsernameField組件來進行較小的改進,該組件的使用將很簡單,並且在兩種情況下均保持一致。 還可以考慮withValidation HOC將錯誤消息添加到字段中。 如果您真的想拉伸它,可以為Formik設置withSubmit HOC,將所有道具傳遞給Formik,渲染子對象(您將通過handleChange prop)和一個提交按鈕。 我假設表單本身使用上下文將狀態傳遞給ErrorMessage和FastField。

我可能會錯過這里未說明的某些復雜性,但這看起來就像創建一個接受幾個傳入屬性的通用功能組件一樣簡單。 我做了一個比較,您只需要在道具中添加“ title”,“ buttonText”和“ type”即可。 您也可以發送initialValues對象作為道具,而不是從'type'派生它。

我的意思是,您嘗試過以下方法嗎?

import React from 'react';
import { Link } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import { Formik, FastField, Form, ErrorMessage } from 'formik';

import PropTypes from 'prop-types';
import { FormDebug } from 'utils/FormDebug';
import * as schema from 'validations/AuthValidationSchema';

function AuthPage({ buttonText, initialValues, title, type, username,
                    onChangeUsername, onSubmitForm }) {
  const authSchema = type === 'login' 
        ? schema.LoginValidationSchema 
        : schema.ForgotPasswordValidationSchema;

  return (
    <div>
      <Helmet>
        <title>{title}</title>
      </Helmet>
      <Formik
        initialValues={initialValues}
        validationSchema={authSchema}
        onSubmit={onSubmitForm}
        render={({ isSubmitting, isValid, handleChange }) => (
          <Form>
            <FastField
              type="text"
              name="username"
              render={({ field }) => (
                <input
                  {...field}
                  onChange={e => {
                    handleChange(e);
                    onChangeUsername(e);
                  }}
                />
              )}
            />
            <ErrorMessage name="username" component="div" aria-live="polite" />
            {type === 'forgot' &&
              <FastField type="password" name="password" />
              <ErrorMessage name="password" component="div" aria-live="polite" />
            }
            <button type="submit" disabled={isSubmitting || !isValid}>
              {buttonText}
            </button>
            <FormDebug />
          </Form>
        )}
      />
      <Link to="/auth/forgot_password">Forgot Password</Link>
    </div>
  );
}

AuthPage.propTypes = {
  buttonText: PropTypes.string,
  initialValues: PropTypes.object,
  title: PropTypes.string,
  type: PropTypes.oneOf(['login', 'forgot'])
  username: PropTypes.string,
  onSubmitForm: PropTypes.func.isRequired,
  onChangeUsername: PropTypes.func.isRequired,
};

export default AuthPage;

(我不記得的是密碼字段及其ErrorMessage的條件渲染是否需要包裝在div中,而不是使其成為一個元素)

如果您不想傳遞初始值:

  const initialVals = type === 'login'
        ? { username, password: ''}
        : { username }
  ...
  initialValues={initialVals}

並將其從propTypes中刪除

我不確定的另一件事是為什么FormDebug在兩個版本中的放置方式不同。 我把它留按鈕后面

這兩頁有不同的關注點,因此,我不建議您提取可用於兩種情況的邏輯,因為這會通過將代碼進一步分開而增加復雜性,即使它確實刪除了一些重復的行也是如此。 在這種情況下,一個好的策略可能是考慮要在組件內重用的事物。

例如,您可以將formik組件包裝在自己的包裝器中,然后包裝例如與新表單一起使用的輸入組件。 減少樣板的好方法可能是針對此最終結果的某些變體

<CoolForm 
  initialValues={{ username, password: '' }}
  validationSchema={LoginValidationSchema}
>
  <CoolFormInputWithError someProps={{howeverYou: 'want to design this'}}>
  <CoolFormInputWithError someProps={{howeverYou: 'want to design this'}}>
  <CoolFormSubmit>
    Login
  </CoolFormSubmit>
  <FormDebug />
</CoolForm>

那只是一個主意,但是這種策略的好處是,如果您出於某種原因要重構formik,這真的很簡單,因為現在所有代碼都包裝在CoolForm組件中,因此最終只能更改自己的實現組件,盡管這種情況下的好處很小。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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