My App

在事件上获取

在事件上获取

如果你曾经为自己建立过开发者作品集,那么你很可能已经实现了一个联系表单。这些表单在互联网上随处可见。

让我们看看怎么实现一个联系表单

在这个视频中,我们看到如何使用 fetch 向我们的后端 API 发送数据。我们看到如何发送 POST 请求,如何将请求体字符串化,以及如何验证我们是否收到了来自服务器的正确响应。

但实际上,这个实现还不完整。我们需要更新用户界面,以便用户随时知道发生了什么!

这是上面视频中的沙箱:

import React from 'react';

const ENDPOINT =
  'https://jor-test-api.vercel.app/api/contact';

function ContactForm() {
  const [email, setEmail] = React.useState('');
  const [message, setMessage] = React.useState('');

  const id = React.useId();
  const emailId = `${id}-email`;
  const messageId = `${id}-message`;

  async function handleSubmit(event) {
    event.preventDefault();
    
    const response = await fetch(ENDPOINT, {
      method: 'POST',
      body: JSON.stringify({
        email,
        message,
      }),
    });
    const json = await response.json();
    console.log(json);
  }

  return (
    <form onSubmit={handleSubmit}>
      <div className="row">
        <label htmlFor={emailId}>Email</label>
        <input
          required={true}
          id={emailId}
          type="email"
          value={email}
          onChange={(event) => {
            setEmail(event.target.value);
          }}
        />
      </div>
      <div className="row">
        <label htmlFor={messageId}>Message</label>
        <textarea
          required={true}
          id={messageId}
          value={message}
          onChange={(event) => {
            setMessage(event.target.value);
          }}
        />
      </div>
      <div className="button-row">
        <span className="button-spacer" />
        <button>Submit</button>
      </div>
    </form>
  );
}

export default ContactForm;

加载、成功和错误状态

在提交网络请求时,我们希望更新用户界面以指示三种不同的状态:

  • Loading 加载中
  • Success 成功
  • Error 错误

在下面的视频中,我将向你展示我如何实现这些状态,并相应地更新用户界面,但我鼓励你自己动手尝试,使用上面的演练场。随意按照你想要的方式构建内容,以你认为最合理的方式更新用户界面。

这是我将如何处理它:

在上面的视频中,我们讨论了 HTML 验证。您可以在 MDN 上了解更多:“约束验证”。

我们还讨论了 HTTP 状态码。您可以在“HTTP 状态码”入门课程中了解更多👀。

这是视频中的最终沙盒:

import React from 'react';

// Remove the “?simulatedError=true” to
// stop receiving errors:
const ENDPOINT =
  'https://jor-test-api.vercel.app/api/contact?simulatedError=true';

function ContactForm() {
  const [email, setEmail] = React.useState('');
  const [message, setMessage] = React.useState('');
  
  // idle | loading | success | error
  const [status, setStatus] = React.useState('idle');
  
  const id = React.useId();
  const emailId = `${id}-email`;
  const messageId = `${id}-message`;
  
  async function handleSubmit(event) {
    event.preventDefault();

    setStatus('loading');

    const response = await fetch(ENDPOINT, {
      method: 'POST',
      body: JSON.stringify({
        email,
        message,
      }),
    });
    const json = await response.json();

    if (json.ok) {
      setStatus('success');
      setMessage('');
    } else {
      setStatus('error');
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <div className="row">
        <label htmlFor={emailId}>Email</label>
        <input
          required={true}
          disabled={status === 'loading'}
          id={emailId}
          type="email"
          value={email}
          onChange={(event) => {
            setEmail(event.target.value);
          }}
        />
      </div>
      <div className="row">
        <label htmlFor={messageId}>Message</label>
        <textarea
          required={true}
          disabled={status === 'loading'}
          id={messageId}
          value={message}
          onChange={(event) => {
            setMessage(event.target.value);
          }}
        />
      </div>
      <div className="button-row">
        <span className="button-spacer" />
        <button disabled={status === 'loading'}>
          {status === 'loading'
            ? 'Submitting…'
            : 'Submit'}
        </button>
      </div>
      {status === 'success' && <p>Message sent!</p>}
      {status === 'error' && (
        <p>Something went wrong!</p>
      )}
    </form>
  );
}

export default ContactForm;

捕获意外错误

在上面的例子中,我们通过检查 JSON 响应的值来处理预期的错误,但我们没有处理意外的错误。例如,如果服务器宕机,整个过程将抛出异常。

为了防范这种情况,我们还应该将获取请求包裹在 try / catch 中:

async function handleSubmit(event) {
  event.preventDefault();
 
  setStatus("loading");
 
  try {
    const response = await fetch(ENDPOINT, {
      method: "POST",
      body: JSON.stringify({
        email,
        message,
      }),
    });
    const json = await response.json();
 
    if (json.ok) {
      setStatus("success");
      setMessage("");
    } else {
      setStatus("error");
    }
  } catch (err) {
    setStatus("error");
  }
}

On this page