【问题标题】:React repeated onInput handlers in components反应组件中重复的 onInput 处理程序
【发布时间】:2020-02-25 08:31:05
【问题描述】:

我有一个如下所示的 react 组件。

表单输入由onInputChange函数处理,表单提交由onFormSubmit处理

function RegisterForm() {
  // formData stores all the register form inputs.
  const [formData, setFormData] = useState(registerDefault);
  const [errors, posting, postData] = useDataPoster();

  function onInputChange(event: ChangeEvent<HTMLInputElement>) {
    let update = { [event.target.name]: event.target.value };

    setFormData(oldForm => Object.assign(oldForm, update));
  }

  function onFormSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();

    const onSuccess: AxiosResponseHandler = response => {
      setFormData(Object.assign(formData, response.data));
    };

    postData("/api/register", formData, onSuccess);
  }

  return (
    <form onSubmit={onFormSubmit}>
      <FormTextInput
        name="full_name"
        label="Name"
        errors={errors.full_name}
        onChange={onInputChange}
      />
      <FormTextInput
        name="email"
        label="Email address"
        type="email"
        errors={errors.email}
        onChange={onInputChange}
      />
      <button type="submit" className="theme-btn submit" disabled={posting}>
        {posting && <span className="fas fa-spin fa-circle-notch"></span>}
        Create
      </button>
    </form>
  );
}

我的应用程序有 50 多个类似的表单,我想知道是否必须将这两个功能复制粘贴到所有其他表单上。 onInputChange 不会有任何变化,并且 url 是 onFormSubmit 中唯一的变量。

我正在考虑一种基于类的方法,将setFormDatapostData 作为属性,将相关函数作为类方法。但在这种情况下,我必须 bind 带有类实例的处理程序,以便处理程序具有有效的 this 实例。

还有其他方法可以做到这一点吗?您如何避免在所有表单组件中重复这两个代码块?

谢谢

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    您可以创建一个自定义挂钩,如下所示:

    const [formState, setFormState] = useFormStateHandler({name: ''})
    
    <input value={formState.name} onChange={event => setFormState(event, 'name')} />
    

    定义如下所示:

    export default function useFormStateHandler(initialState) {
      const [state, setState] = useState(initialState)
    
      const updater = (event, name) => {
        setState({...state, [name]: event.target.value})
      }
    
      return [state, updater]
    }
    

    【讨论】:

    • 这看起来不错。如果我也返回setState,那么我将能够直接更改组件中的状态(如果需要)。我正在等待所有可能的选择。您还有其他解决方案吗?您如何看待我提到的基于类的方法。这样做有什么坏处吗?
    • 基于类的 HOC 或像这样的自定义钩子都可以完成相同的工作,我只是更喜欢在钩子中编写,因为它出现了,是的,你需要一些自定义状态更新 setState也可以通过。
    • 这个答案纯粹是根据个人喜好选择的。创建自定义钩子看起来更干净,尤其是当我的应用程序已经尽可能使用钩子时。
    【解决方案2】:

    创建一个 HOC 以将输入处理程序注入表单组件,并为 url 添加参数。

    function RegisterForm(props) {
      // specific function
      const specific = () => {
        const formData = props.formData; // use passed state values
        // use form data
      }
    }
    
    function withInputHandlers(Component, params) {
      return function(props) {
          // states
          function onInputChange(...) {...}
          function onFormSubmit(...) {
            // use params.url when submitting
            postData(params.url, formData, onSuccess);
          }
          // inject input handlers to component and state values
          return (
            <Component {...props} formData={formData} onChange={onInputChange} onSubmit={onFormSubmit} />
          );
      }
    }
    
    // Usage
    const EnhancedRegisterForm = withInputHandlers(
      RegisterForm,
      { url: 'register_url' } // params
    );
    
    const EnhancedSurveyForm = withInputHandlers(
      Survey,
      { url: 'survey_url' } // params
    )
    

    【讨论】:

    • 我喜欢这种方法。但是这种外观已关闭以供更新。如果有其他特定于组件的功能并且需要访问状态怎么办?
    • @KaleshKaladharan,您还可以将状态注入组件。对于特定于组件的其他功能将仅在组件本身内部。我已经更新了显示这样的代码。
    【解决方案3】:

    此更改可能会对您有所帮助

    function RegisterForm() {
      // formData stores all the register form inputs.
      const [formData, setFormData] = useState(registerDefault);
      const [errors, posting, postData] = useDataPoster();
    
      const onInputChange = name => event => {
        let update = { [name]: event.target.value };
    
        setFormData(oldForm => Object.assign(oldForm, update));
      }
    
      const onFormSubmit = url => event =>{
        event.preventDefault();
    
        const onSuccess: AxiosResponseHandler = response => {
          setFormData(Object.assign(formData, response.data));
        };
    
        postData(url, formData, onSuccess);
      }
    
      return (
        <form onSubmit={onFormSubmit("/api/register")}>
          <FormTextInput
            name="full_name"
            label="Name"
            errors={errors.full_name}
            onChange={onInputChange("full_name")}
          />
          <FormTextInput
            name="email"
            label="Email address"
            type="email"
            errors={errors.email}
            onChange={onInputChange("email")}
          />
          <button type="submit" className="theme-btn submit" disabled={posting}>
            {posting && <span className="fas fa-spin fa-circle-notch"></span>}
            Create
          </button>
        </form>
      );
    }
    

    【讨论】:

    • 我想你没有理解我的问题。我试图与多个组件共享onInputChangeonFormSubmit 代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-09
    • 1970-01-01
    • 1970-01-01
    • 2021-01-07
    • 1970-01-01
    相关资源
    最近更新 更多