EmailVerify LogoEmailVerify

React

Email checker for React. Real-time email verification in React forms and components.

为您的 React 应用添加实时电子邮件验证。在用户输入时验证电子邮件,防止无效提交,提升表单用户体验。

安装

npm install @emailverify/react @emailverify/node
yarn add @emailverify/react @emailverify/node
pnpm add @emailverify/react @emailverify/node

快速开始

带验证的基本邮箱输入

import { useState } from 'react';
import { useEmailVerification } from '@emailverify/react';

function SignupForm() {
  const [email, setEmail] = useState('');
  const { verify, result, isLoading, error } = useEmailVerification();

  const handleEmailBlur = async () => {
    if (email) {
      await verify(email);
    }
  };

  return (
    <form>
      <div className="form-group">
        <label htmlFor="email">电子邮件</label>
        <input
          id="email"
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          onBlur={handleEmailBlur}
          className={result?.status === 'invalid' ? 'error' : ''}
        />

        {isLoading && <span className="loading">验证中...</span>}

        {result?.status === 'invalid' && (
          <span className="error-message">
            请输入有效的电子邮件地址
          </span>
        )}

        {result?.status === 'valid' && (
          <span className="success-message">✓ 电子邮件已验证</span>
        )}

        {result?.result?.disposable && (
          <span className="warning-message">
            请使用永久电子邮件地址
          </span>
        )}
      </div>

      <button type="submit" disabled={result?.status !== 'valid'}>
        注册
      </button>
    </form>
  );
}

Provider 设置

使用 EmailVerify provider 包装您的应用:

// App.jsx or main.jsx
import { EmailVerifyProvider } from '@emailverify/react';

function App() {
  return (
    <EmailVerifyProvider
      config={{
        // API 请求通过您的后端
        apiEndpoint: '/api/verify-email',
      }}
    >
      <YourApp />
    </EmailVerifyProvider>
  );
}

永远不要在客户端代码中暴露您的 API 密钥。始终通过后端验证邮箱。

后端 API 路由

创建安全的邮箱验证 API 端点:

// pages/api/verify-email.js (Next.js) or your Express route
import { EmailVerify } from '@emailverify/node';

const client = new EmailVerify({
  apiKey: process.env.EMAILVERIFY_API_KEY,
});

export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  const { email } = req.body;

  if (!email) {
    return res.status(400).json({ error: 'Email is required' });
  }

  try {
    const result = await client.verify(email);
    return res.json(result);
  } catch (error) {
    console.error('Verification error:', error);
    return res.status(500).json({ error: 'Verification failed' });
  }
}

Hooks 参考

useEmailVerification

邮箱验证的主要 hook:

const {
  verify,      // 验证电子邮件的函数
  result,      // 验证结果对象
  isLoading,   // 加载状态
  error,       // 验证失败时的错误对象
  reset,       // 重置状态
} = useEmailVerification(options);

选项:

interface UseEmailVerificationOptions {
  // 防抖延迟(毫秒,默认:500)
  debounceMs?: number;

  // 值更改时自动验证
  autoVerify?: boolean;

  // 缓存结果的持续时间(毫秒)
  cacheDuration?: number;

  // 自定义 API 端点
  apiEndpoint?: string;
}

useEmailInput

将输入状态与验证结合的便捷 hook:

import { useEmailInput } from '@emailverify/react';

function EmailField() {
  const {
    value,
    onChange,
    onBlur,
    verification,
    isValid,
    errorMessage,
  } = useEmailInput({
    debounceMs: 300,
    validateOnBlur: true,
  });

  return (
    <div>
      <input
        type="email"
        value={value}
        onChange={onChange}
        onBlur={onBlur}
      />
      {errorMessage && <span className="error">{errorMessage}</span>}
    </div>
  );
}

组件

EmailInput 组件

预构建的带验证的邮箱输入:

import { EmailInput } from '@emailverify/react';

function MyForm() {
  const handleVerified = (result) => {
    console.log('Verification result:', result);
  };

  return (
    <EmailInput
      name="email"
      label="Email Address"
      placeholder="you@example.com"
      onVerified={handleVerified}
      showValidationStatus
      blockDisposable
      blockRoleBased={false}
      className="custom-input"
    />
  );
}

Props:

PropTypeDefaultDescription
onVerifiedfunction-Callback when verification completes
showValidationStatusbooleantrueShow validation indicators
blockDisposablebooleantrueShow error for disposable emails
blockRoleBasedbooleanfalseShow error for role-based emails
verifyOnBlurbooleantrueVerify when field loses focus
debounceMsnumber500Debounce delay

表单库集成

React Hook Form

import { useForm } from 'react-hook-form';
import { useEmailVerification } from '@emailverify/react';

function SignupForm() {
  const {
    register,
    handleSubmit,
    setError,
    clearErrors,
    formState: { errors },
  } = useForm();

  const { verify, isLoading } = useEmailVerification();

  const validateEmail = async (email) => {
    const result = await verify(email);

    if (result.status === 'invalid') {
      setError('email', {
        type: 'validation',
        message: '请输入有效的电子邮件地址',
      });
      return false;
    }

    if (result.result?.disposable) {
      setError('email', {
        type: 'validation',
        message: '不允许使用一次性电子邮件地址',
      });
      return false;
    }

    clearErrors('email');
    return true;
  };

  const onSubmit = async (data) => {
    const isValid = await validateEmail(data.email);
    if (!isValid) return;

    // 继续提交表单
    console.log('提交:', data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        type="email"
        {...register('email', {
          required: '电子邮件是必需的',
          pattern: {
            value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
            message: '无效的电子邮件格式',
          },
        })}
        onBlur={(e) => validateEmail(e.target.value)}
      />
      {errors.email && <span>{errors.email.message}</span>}
      {isLoading && <span>验证中...</span>}

      <button type="submit">提交</button>
    </form>
  );
}

Formik

import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as Yup from 'yup';
import { useEmailVerification } from '@emailverify/react';

function SignupForm() {
  const { verify } = useEmailVerification();

  const validationSchema = Yup.object({
    email: Yup.string()
      .email('Invalid email format')
      .required('Email is required')
      .test('email-verification', 'Invalid email address', async (value) => {
        if (!value) return false;
        const result = await verify(value);
        return result.status === 'valid';
      }),
  });

  return (
    <Formik
      initialValues={{ email: '' }}
      validationSchema={validationSchema}
      onSubmit={(values) => console.log(values)}
    >
      {({ isSubmitting, isValidating }) => (
        <Form>
          <Field type="email" name="email" />
          <ErrorMessage name="email" component="span" className="error" />
          {isValidating && <span>验证中...</span>}

          <button type="submit" disabled={isSubmitting || isValidating}>
            提交
          </button>
        </Form>
      )}
    </Formik>
  );
}

Zod + React Hook Form

import { z } from 'zod';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { useEmailVerification } from '@emailverify/react';

const schema = z.object({
  email: z.string().email('无效的电子邮件格式'),
  name: z.string().min(2, '名称至少需要 2 个字符'),
});

type FormData = z.infer<typeof schema>;

function SignupForm() {
  const { verify, result } = useEmailVerification();

  const {
    register,
    handleSubmit,
    formState: { errors },
    setError,
  } = useForm<FormData>({
    resolver: zodResolver(schema),
  });

  const onSubmit = async (data: FormData) => {
    // Verify email before submission
    const verification = await verify(data.email);

    if (verification.status !== 'valid') {
      setError('email', {
        type: 'manual',
        message: 'Please enter a valid email address',
      });
      return;
    }

    // Submit form
    console.log('Submitting:', data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input type="email" {...register('email')} />
      {errors.email && <span>{errors.email.message}</span>}

      <input type="text" {...register('name')} />
      {errors.name && <span>{errors.name.message}</span>}

      <button type="submit">Submit</button>
    </form>
  );
}

防抖和性能

内置防抖

The hook includes debouncing by default:

const { verify } = useEmailVerification({
  debounceMs: 500, // Wait 500ms after user stops typing
});

// Verify on every keystroke (debounced internally)
<input
  type="email"
  onChange={(e) => verify(e.target.value)}
/>

手动防抖

For more control, use your own debouncing:

import { useDeferredValue, useEffect } from 'react';

function EmailField() {
  const [email, setEmail] = useState('');
  const deferredEmail = useDeferredValue(email);
  const { verify, result } = useEmailVerification();

  useEffect(() => {
    if (deferredEmail && deferredEmail.includes('@')) {
      verify(deferredEmail);
    }
  }, [deferredEmail]);

  return (
    <input
      type="email"
      value={email}
      onChange={(e) => setEmail(e.target.value)}
    />
  );
}

缓存结果

缓存验证结果以避免冗余 API 调用:

const { verify } = useEmailVerification({
  cacheDuration: 3600000, // 缓存 1 小时
});

样式

CSS 类

EmailInput 组件应用这些类:

/* 容器 */
.bv-email-input { }

/* 输入字段 */
.bv-email-input__field { }
.bv-email-input__field--valid { }
.bv-email-input__field--invalid { }
.bv-email-input__field--loading { }

/* 状态指示器 */
.bv-email-input__status { }
.bv-email-input__status--valid { }
.bv-email-input__status--invalid { }
.bv-email-input__status--warning { }

/* 错误消息 */
.bv-email-input__error { }

自定义样式示例

.bv-email-input__field {
  padding: 12px 16px;
  border: 2px solid #e2e8f0;
  border-radius: 8px;
  font-size: 16px;
  transition: border-color 0.2s;
}

.bv-email-input__field:focus {
  outline: none;
  border-color: #3182ce;
}

.bv-email-input__field--valid {
  border-color: #48bb78;
}

.bv-email-input__field--invalid {
  border-color: #f56565;
}

.bv-email-input__status {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 14px;
  margin-top: 4px;
}

.bv-email-input__status--valid {
  color: #48bb78;
}

.bv-email-input__status--invalid {
  color: #f56565;
}

Tailwind CSS

<EmailInput
  className="w-full px-4 py-3 border-2 rounded-lg focus:ring-2 focus:ring-blue-500"
  inputClassName="data-[valid]:border-green-500 data-[invalid]:border-red-500"
  statusClassName="text-sm mt-1"
/>

TypeScript 支持

完整的 TypeScript 支持和导出类型:

import type {
  VerificationResult,
  EmailVerificationOptions,
  UseEmailVerificationReturn,
} from '@emailverify/react';

interface FormState {
  email: string;
  verification: VerificationResult | null;
}

function useSignupForm(): FormState {
  const [email, setEmail] = useState('');
  const { result } = useEmailVerification();

  return {
    email,
    verification: result,
  };
}

类型定义

interface VerificationResult {
  email: string;
  status: 'valid' | 'invalid' | 'unknown' | 'accept_all';
  result: {
    deliverable: boolean;
    valid_format: boolean;
    valid_domain: boolean;
    valid_mx: boolean;
    disposable: boolean;
    role: boolean;
    catchall: boolean;
    free: boolean;
    smtp_valid: boolean;
  };
  score: number;
  reason: string | null;
}

interface UseEmailVerificationReturn {
  verify: (email: string) => Promise<VerificationResult>;
  result: VerificationResult | null;
  isLoading: boolean;
  error: Error | null;
  reset: () => void;
}

最佳实践

1. 在失焦时验证,而不是每次按键

// ✅ 好的 - 在用户完成输入时验证
<input
  type="email"
  onBlur={(e) => verify(e.target.value)}
/>

// ❌ 避免 - 过多的 API 调用
<input
  type="email"
  onChange={(e) => verify(e.target.value)} // 没有防抖
/>

2. 显示清晰的反馈

function EmailStatus({ result, isLoading }) {
  if (isLoading) {
    return <span className="text-gray-500">检查电子邮件...</span>;
  }

  if (!result) return null;

  const messages = {
    valid: { text: '✓ 有效的电子邮件', className: 'text-green-600' },
    invalid: { text: '✗ 无效的电子邮件', className: 'text-red-600' },
    unknown: { text: '? 无法验证', className: 'text-yellow-600' },
  };

  const { text, className } = messages[result.status] || {};

  return <span className={className}>{text}</span>;
}

3. 不要阻止表单提交

允许提交,即使验证仍在进行中:

function handleSubmit(data) {
  if (verificationResult?.status === 'invalid') {
    // 显示错误但不阻止
    showWarning('电子邮件可能无效');
  }

  // 继续提交
  submitForm(data);
}

4. 处理网络错误

function EmailField() {
  const { verify, result, error, isLoading } = useEmailVerification();

  return (
    <div>
      <input type="email" onBlur={(e) => verify(e.target.value)} />

      {error && (
        <span className="text-yellow-600">
          无法验证电子邮件。继续提交。
        </span>
      )}
    </div>
  );
}

相关资源

On this page