Home / Hướng dẫn / Tích hợp biểu mẫu liên hệ React (Ứng dụng Vite & một trang)
HƯỚNG DẪN TÍCH HỢP · REACT & SPA

Tích hợp biểu mẫu liên hệ React (Ứng dụng Vite & một trang)

Cách thêm biểu mẫu liên hệ và phản hồi vào Ứng dụng một trang React (SPA) được xây dựng bằng Vite hoặc Tạo ứng dụng React, có tính năng chống spam honeypot và thông báo hộp thư đến tức thì.

1. Thành phần biểu mẫu liên hệ React tiêu chuẩn với Vite

Thành phần React nhẹ này sử dụng móc React tiêu chuẩn (useState) và API tìm nạp của trình duyệt để gửi trực tiếp đến điểm cuối công khai Lugatu của bạn. Không cần thư viện biểu mẫu bên ngoài hoặc máy chủ phụ trợ.

src/components/ContactForm.tsx tsx
import React, { useState } from 'react';

export function ContactForm() {
  const [formData, setFormData] = useState({ name: '', email: '', message: '', _gotcha: '' });
  const [status, setStatus] = useState<'idle' | 'submitting' | 'success' | 'error'>('idle');
  const [errorMessage, setErrorMessage] = useState('');

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    if (formData._gotcha) return; // Silent discard for automated bot traps

    setStatus('submitting');
    setErrorMessage('');

    try {
      const response = await fetch('https://www.lugatu.com/api/v1/public/forms/YOUR_PUBLIC_FORM_ID/submissions', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
        },
        body: JSON.stringify({
          fields: {
            name: formData.name,
            email: formData.email,
            message: formData.message,
          },
          honeypot: formData._gotcha,
        }),
      });

      if (!response.ok) {
        throw new Error('Failed to deliver message');
      }

      setStatus('success');
      setFormData({ name: '', email: '', message: '', _gotcha: '' });
    } catch (err: any) {
      setStatus('error');
      setErrorMessage(err.message || 'Submission error. Please try again.');
    }
  };

  return (
    <form onSubmit={handleSubmit} className="space-y-4 max-w-md">
      <div>
        <label htmlFor="react-name" className="block text-sm font-semibold mb-1">Your Name</label>
        <input
          id="react-name"
          type="text"
          required
          value={formData.name}
          onChange={(e) => setFormData({ ...formData, name: e.target.value })}
          className="w-full border rounded-xl p-2.5"
        />
      </div>

      <div>
        <label htmlFor="react-email" className="block text-sm font-semibold mb-1">Email Address</label>
        <input
          id="react-email"
          type="email"
          required
          value={formData.email}
          onChange={(e) => setFormData({ ...formData, email: e.target.value })}
          className="w-full border rounded-xl p-2.5"
        />
      </div>

      <div>
        <label htmlFor="react-message" className="block text-sm font-semibold mb-1">Message</label>
        <textarea
          id="react-message"
          rows={4}
          required
          value={formData.message}
          onChange={(e) => setFormData({ ...formData, message: e.target.value })}
          className="w-full border rounded-xl p-2.5"
        />
      </div>

      <!-- Honeypot spam defense -->
      <input
        type="text"
        name="_gotcha"
        value={formData._gotcha}
        onChange={(e) => setFormData({ ...formData, _gotcha: e.target.value })}
        style={{ display: 'none' }}
        tabIndex={-1}
        autoComplete="off"
      />

      <button
        type="submit"
        disabled={status === 'submitting'}
        className="btn-primary-editorial py-2.5 px-6"
      >
        {status === 'submitting' ? 'Sending...' : 'Send Message'}
      </button>

      {status === 'success' && (
        <p className="text-sm text-green-700 font-medium">Thank you! Your message was delivered.</p>
      )}
      {status === 'error' && (
        <p className="text-sm text-red-600">{errorMessage}</p>
      )}
    </form>
  );
}

2. Tích hợp với React Hook Form và Formik

Mô hình gửi JSON tiêu chuẩn Lugatu tích hợp dễ dàng với các thư viện xác thực biểu mẫu như React Hook Form, Formik hoặc Zod. Truyền trực tiếp các giá trị được tuần tự hóa vào đối tượng tải trọng của trường.

useLugatuForm.ts typescript
// React Hook Form submission handler
const onSubmit = async (data: FormValues) => {
  const res = await fetch(`https://www.lugatu.com/api/v1/public/forms/${import.meta.env.VITE_LUGATU_FORM_ID}/submissions`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ fields: data }),
  });
  return res.ok;
};

3. Biến môi trường & Phân tách bí mật trong React

Trong Vite, các biến công khai có tiền tố là VITE_. Sử dụng VITE_LUGATU_FORM_ID cho ID biểu mẫu công khai. Không bao giờ để lộ khóa API từ máy chủ đến máy chủ (lug_live_...) bên trong mã nguồn hoặc kho lưu trữ phía máy khách.

warning

Khóa API trực tiếp bắt đầu bằng lug_live_ được dành riêng để thực thi từ máy chủ đến máy chủ. Mọi yêu cầu trình duyệt được gửi bằng khóa API trực tiếp đều bị từ chối bằng HTTP 403.

Bảo mật & xác thực bất biến

Bất biến bảo mật: Không bao giờ thêm tiền tố các khóa API trực tiếp bằng VITE_ hoặc REACT_APP_. Máy khách trình duyệt phải gửi độc quyền tới các điểm cuối công khai mà không cần khóa API.

Khắc phục sự cố và các lỗi nhập thường gặp

Việc gửi biểu mẫu trả về lỗi CORS trong bảng điều khiển trình duyệt

Đảm bảo bạn đang gửi tới điểm cuối công khai (https://www.lugatu.com/api/v1/public/forms/YOUR_PUBLIC_FORM_ID/submissions). Điểm cuối từ máy chủ đến máy chủ (POST /api/v1/forms/{id}/submissions) chặn rõ ràng nguồn gốc của trình duyệt.

Câu hỏi thường gặp

Tôi có thể sử dụng React Hook Form hoặc Formik với Lugatu không?

Đúng. Lugatu chấp nhận JSON tiêu chuẩn với đối tượng trường. Bất kỳ thư viện biểu mẫu React nào cũng có thể tuần tự hóa các giá trị của nó và gửi đến điểm cuối công khai.

Bạn đã sẵn sàng tạo điểm cuối biểu mẫu chưa?

Bắt đầu miễn phí trong vài giây. Không cần thẻ tín dụng.

Bắt đầu miễn phí →