简体   繁体   English

使用自定义 React 钩子提交带有数据的表单

[英]Submit a form with data using a custom React hook

I'm having trouble figuring this out.我很难弄清楚这一点。 I want to create a hook that's called to submit a form using fetch.我想创建一个称为使用 fetch 提交表单的钩子。

This is what I have right now.这就是我现在所拥有的。 The component holding the form:持有表单的组件:

const MyForm = (): ReactElement => {
    const [status, data] = useSubmitForm('https://myurl-me/', someData);
    
    return <>
        <div className='Feedback-form'>
            <div className='body'>
                <form>
                    <input type='text' name='username' placeholder='name' required />
                    <input type='email' name='email' placeholder='email' required />
                    <button className='submit-feedback-button' type='button'>Send feedback</button>
                </form>
            </div>
        </div>
    </>
}

The custom hook:自定义钩子:

import { useState, useEffect } from 'react';

const useSubmitForm = (url: string, data: URLSearchParams): [string, []] => {

    const [status, setStatus] = useState<string>('idle');
    const [responseData, setData] = useState<[]>([]);

    useEffect(() => {
        if (!url) return;

        const fetchData = async () => {
            setStatus('fetching');

            const response = await fetch(url, {      
                method: 'POST',
                headers: {
                    'Accept': 'text/html',
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                body: data
            });

            const data = await response.json();

            setData(data);
            setStatus('fetched');
        };

        fetchData();
    }, [url]);

    return [status, responseData];
};

export default useSubmitForm;

My problem is that I think this hook is being called right away.我的问题是我认为这个钩子正在被立即调用。 How do I make this hook and call it in such a way that it's only called when the form is submitted and all the data I need to send in the request body is there to be included?如何制作这个钩子并以仅在提交表单并且我需要在请求正文中发送的所有数据都包含在内的方式调用它?

You are correct, the effect runs once when the component mounts and since url is truthy, it skips the early return and invokes fetchData .你是对的,当组件安装时效果会运行一次,并且由于url是真实的,它会跳过早期返回并调用fetchData

How do I make this hook and call it in such a way that it's only called when the form is submitted and all the data I need to send in the request body is there to be included?如何制作这个钩子并以仅在提交表单并且我需要在请求正文中发送的所有数据都包含在内的方式调用它?

You need to also return a function for the component to invoke and pass along the form field values.您还需要返回 function 以供组件调用并传递表单字段值。 I think you've a couple basic options.我认为你有几个基本的选择。

  1. Convert the form fields to be controlled inputs and store the field state in the component and invoke a "fetch" function returned from the useSubmitForm hook.将表单字段转换为受控输入并将字段 state 存储在组件中,并调用从useSubmitForm挂钩返回的“获取”function。
  2. Return an onSubmit handler from the useSubmitForm to attach to your form element.useSubmitForm返回一个onSubmit处理程序以附加到您的form元素。 The onSubmit handler would need to know what fields to access from the onSubmit event though, so passing an array of field names to the hook (ie a "config") makes sense. onSubmit处理程序需要知道从onSubmit事件访问哪些字段,因此将字段名称数组传递给钩子(即“配置”)是有意义的。

Solution 1 - Use controlled inputs and returned fetch function解决方案 1 - 使用受控输入并返回获取 function

Unwrap the fetchData function from the useEffect hook and add a form field data parameter to it.useEffect挂钩中fetchData function 并向其添加表单字段数据参数。 Since fetch and response.json() can both throw errors/rejections you should surround this block in a try/catch.由于fetchresponse.json()都可以抛出错误/拒绝,你应该在 try/catch 中包围这个块。 Return the custom fetchData function for the form to invoke.返回自定义fetchData function 以供表单调用。

useSubmitForm使用提交表单

const useSubmitForm = (
  url: string,
  data: URLSearchParams
): [function, string, []] => {
  const [status, setStatus] = useState<string>("idle");
  const [responseData, setData] = useState<[]>([]);

  const fetchData = async (formData) => {
    setStatus("fetching");

    try {
      const response = await fetch(url, {
        method: "POST",
        headers: {
          Accept: "text/html",
          "Content-Type": "application/x-www-form-urlencoded"
        },
        body: JSON.stringify(formData)
      });

      const data = await response.json();

      setData(data);
      setStatus("fetched");
    } catch (err) {
      setData(err);
      setStatus("failed");
    }
  };

  return [fetchData, status, responseData];
};

MyForm我的表格

const MyForm = (): ReactElement => {
  const [fields, setFields] = useState({ // <-- create field state
    email: '',
    username: '',
  });
  const [fetchData, status, data] = useSubmitForm(
    "https://myurl-me/",
    someData
  );

  useEffect(() => {
    // handle successful/failed fetch status and data/error
  }, [status, data]);

  const changeHandler = (e) => {
    const { name, value } = e.target;
    setFields((fields) => ({
      ...fields,
      [name]: value
    }));
  };

  const submitHandler = (e) => {
    e.preventDefault();
    fetchData(fields); // <-- invoke hook fetchData function
  };

  return (
    <div className="Feedback-form">
      <div className="body">
        <form onSubmit={submitHandler}> // <-- attach submit handler
          <input
            type="text"
            name="username"
            placeholder="name"
            onChange={changeHandler} // <-- attach change handler
            value={fields.username}  // <-- pass state
          />
          <input
            type="email"
            name="email"
            placeholder="email"
            onChange={changeHandler}  // <-- attach change handler
            value={fields.email}  // <-- attach state
          />
          <button className="submit-feedback-button" type="submit">
            Send feedback
          </button>
        </form>
      </div>
    </div>
  );
};

Solution 2 - Return an onSubmit handler and pass an array of fields to the useSubmitForm解决方案 2 - 返回一个onSubmit处理程序并将一个字段数组传递给useSubmitForm

useSubmitForm使用提交表单

const useSubmitForm = (
  url: string,
  data: URLSearchParams,
  fields: string[],
): [function, string, []] => {
  const [status, setStatus] = useState<string>("idle");
  const [responseData, setData] = useState<[]>([]);

  const fetchData = async (formData) => {
    setStatus("fetching");

    try {
      const response = await fetch(url, {
        method: "POST",
        headers: {
          Accept: "text/html",
          "Content-Type": "application/x-www-form-urlencoded"
        },
        body: JSON.stringify(formData)
      });

      const data = await response.json();

      setData(data);
      setStatus("fetched");
    } catch (err) {
      setData(err);
      setStatus("failed");
    }
  };

  const onSubmit = e => {
    e.preventDefault();

    const formData = fields.reduce((formData, field) => ({
      ...formData,
      [field]: e.target[field].value,
    }), {});
    fetchData(formData);
  }

  return [onSubmit, status, responseData];
};

MyForm我的表格

const MyForm = (): ReactElement => {
  const [onSubmit, status, data] = useSubmitForm(
    "https://myurl-me/",
    someData,
    ['email', 'username'] // <-- pass field array
  );

  useEffect(() => {
    // handle successful/failed fetch status and data/error
  }, [status, data]);

  return (
    <div className="Feedback-form">
      <div className="body">
        <form onSubmit={onSubmit}> // <-- attach submit handler
          <input
            type="text"
            name="username"
            placeholder="name"
          />
          <input
            type="email"
            name="email"
            placeholder="email"
          />
          <button className="submit-feedback-button" type="submit">
            Send feedback
          </button>
        </form>
      </div>
    </div>
  );
};

编辑 submit-a-form-with-data-using-a-custom-react-hook

In my opinion the second solution is the cleaner solution and requires less on consuming components to use.在我看来,第二种解决方案是更清洁的解决方案,并且需要更少的消耗组件来使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM