Отправка формы с данными с помощью настраиваемого перехвата реакции

#javascript #reactjs #typescript #react-hooks

#javascript #reactjs #typescript #реагирующие крючки

Вопрос:

Мне никак не удается разобраться в этом. Я хочу создать хук, который вызывается для отправки формы с помощью выборки.

Это то, что у меня есть прямо сейчас. Компонент, содержащий форму:

 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>
    </>
}
 

Пользовательский перехват:

 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;
 

Моя проблема в том, что я думаю, что этот хук вызывается сразу. Как мне создать этот перехват и вызвать его таким образом, чтобы он вызывался только тогда, когда форма отправлена и все данные, которые мне нужно отправить в теле запроса, должны быть включены?

Ответ №1:

Вы правы, эффект запускается один раз, когда компонент монтируется, и, поскольку url он соответствует действительности, он пропускает ранний возврат и вызывает fetchData .

Как мне создать этот перехват и вызвать его таким образом, чтобы он вызывался только тогда, когда форма отправлена и все данные, которые мне нужно отправить в теле запроса, должны быть включены?

Вам также необходимо вернуть функцию для вызова компонента и передачи значений полей формы. Я думаю, у вас есть пара основных вариантов.

  1. Преобразуйте поля формы в управляемые входные данные и сохраните состояние поля в компоненте и вызовите функцию «выборки», возвращаемую из useSubmitForm перехвата.
  2. Верните onSubmit обработчик из useSubmitForm to attach к вашему form элементу. onSubmit Обработчику необходимо будет знать, к каким полям обращаться из onSubmit события, поэтому передача массива имен полей в перехват (т. Е. «Конфигурации») имеет смысл.

Решение 1 — Используйте управляемые входные данные и возвращаемую функцию выборки

Разверните fetchData функцию из useEffect перехвата и добавьте к ней параметр данных поля формы. Поскольку fetch response.json() оба и могут выдавать ошибки / отклонения, вы должны окружить этот блок в try / catch . Верните пользовательскую fetchData функцию для формы для вызова.

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>
  );
};
 

Решение 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>
  );
};
 

Редактировать отправку формы с данными, используя пользовательский перехват реакции

На мой взгляд, второе решение является более чистым решением и требует меньше расходных компонентов для использования.

Комментарии:

1. Большое вам спасибо, я выбрал ваше второе решение. Но все же один вопрос. Я понимаю, для чего используется useEffect (и вы описываете это в своем сообщении), однако, что именно я должен делать в useEffect() для обработки статуса успешной / неудачной выборки и данных / ошибок? Не могли бы вы привести небольшой пример?

2. @erol_smsr Конечно, много раз при обработке данных формы вы хотите выполнить определенные действия после успешной или неудачной отправки. Например, если отправка формы завершилась неудачно, вы можете отобразить пользователю сообщение об ошибке или, в случае успеха, уведомить пользователя тостом и перейти на другую страницу.