使用自定义 React hook 提交包含数据的表单

ero*_*msr 3 javascript typescript reactjs react-hooks

我很难弄清楚这一点。我想创建一个钩子,调用它来使用 fetch 提交表单。

这就是我现在所拥有的。持有表单的组件:

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>
    </>
}
Run Code Online (Sandbox Code Playgroud)

自定义钩子:

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;
Run Code Online (Sandbox Code Playgroud)

我的问题是我认为这个钩子会立即被调用。如何创建这个钩子并以这样的方式调用它:仅在提交表单并且我需要在请求正文中发送的所有数据都包含在内时才调用它?

Dre*_*ese 6

您是对的,该效果在组件安装时运行一次,并且由于url是 true,因此它会跳过早期返回并调用fetchData.

如何创建这个钩子并以这样的方式调用它:仅在提交表单并且我需要在请求正文中发送的所有数据都包含在内时才调用它?

您还需要返回一个函数,以便组件调用并传递表单字段值。我认为你有几个基本选择。

  1. 将表单字段转换为受控输入,并将字段状态存储在组件中,并调用从挂钩返回的“获取”函数useSubmitForm
  2. onSubmit从 中返回一个处理程序useSubmitForm以附加到您的form元素。不过,处理程序onSubmit需要知道要从事件访问哪些字段onSubmit,因此将字段名称数组传递给挂钩(即“配置”)是有意义的。

解决方案 1 - 使用受控输入和返回的获取函数

fetchData从钩子中解开该函数useEffect并向其添加表单字段数据参数。由于fetchresponse.json()都可以抛出错误/拒绝,因此您应该在 try/catch 中包围此块。返回fetchData要调用的表单的自定义函数。

使用提交表单

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];
};
Run Code Online (Sandbox Code Playgroud)

我的表格

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>
  );
};
Run Code Online (Sandbox Code Playgroud)

解决方案 2 - 返回一个onSubmit处理程序并将一组字段传递给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];
};
Run Code Online (Sandbox Code Playgroud)

我的表格

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>
  );
};
Run Code Online (Sandbox Code Playgroud)

使用自定义反应钩子编辑带有数据的提交表单

在我看来,第二种解决方案是更清洁的解决方案,并且需要更少的消耗组件来使用。