You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中Cannot read properties of null及验证码错误问题求助

问题分析与解决方案

问题概述

开发月度服务订阅的手机号验证功能时,提交手机号表单出现两个异常:

  1. 控制台报错 Cannot read properties of null (reading 'style'),指向handlePhoneNumberSubmit函数中操作DOM的代码行
  2. 弹出错误提示“Verification code is incorrect”,但后端测试验证码接口返回200 OK

错误原因分析

  1. DOM操作失败:document.getElementById('phoneNumberForm')返回null,因为Formik组件的id属性不会被渲染到实际DOM节点上,直接通过id获取DOM元素的方法在React组件中不适用。
  2. 状态管理错误:直接修改initialValues常量(initialValues.phoneNumber = values.phoneNumber)不符合React状态管理规范,会导致数据不一致。
  3. 验证码验证逻辑冗余:前端用sentCode === values.verificationCode做二次验证,既多余又可能因为状态同步问题导致误判,应该优先信任后端返回的验证结果。
  4. 表单初始显示状态未控制:验证码表单默认显示,不符合“先提交手机号再验证”的流程逻辑。

修复步骤

  1. 使用React状态管理表单的显示/隐藏状态,替代直接操作DOM
  2. 用useState存储用户输入的手机号,避免修改常量
  3. 调整验证码验证逻辑,仅依赖后端返回结果
  4. 通过条件渲染控制两个表单的显示

完整修复代码

import React, { useState, useEffect } from 'react';
import { Formik, Field, Form, ErrorMessage } from 'formik';
import * as Yup from 'yup';
import axios from 'axios';

const phoneRegExp = /^\d{9}$/;

const initialValues = {
    name: '',
    email: '',
    phoneNumber: '',
    verificationCode: '',
};

const validationSchema = Yup.object({
    name: Yup.string().required('Name is required'),
    email: Yup.string().email('Invalid email address').required('Email is required'),
    phoneNumber: Yup.string().matches(phoneRegExp, 'Phone number must be 9 digits').required('Phone number is required'),
});

const verificationSchema = Yup.object({
    verificationCode: Yup.string().required('Verification code is required'),
});

const VerifyAxios = () => {
    const [verificationCode, setVerificationCode] = useState('');
    const [submitError, setSubmitError] = useState(null);
    const [sentCode, setSentCode] = useState(null);
    // 状态管理表单显示切换
    const [showPhoneForm, setShowPhoneForm] = useState(true);
    // 用状态存储手机号,替代修改initialValues常量
    const [userPhoneNumber, setUserPhoneNumber] = useState('');

    useEffect(() => {
        console.log('Verification Code:', verificationCode);
    }, [verificationCode]);

    const handlePhoneNumberSubmit = async (values, { setSubmitting }) => {
        try {
            const data = {
                imsi: '000702735444444',
                countryCode: '34',
                phoneNumber: values.phoneNumber,
            };
            const response = await axios.post('http://api-m-dev.testing.host:8082/jack.a/api1/1.2.0/requestSimCode', data);
            
            setSubmitting(false);
            setVerificationCode(response.data.verifCode);
            console.log(response.data.verifCode);
            setSubmitError(null);
            setSentCode(response.data.verifCode);
            // 存储手机号到状态
            setUserPhoneNumber(values.phoneNumber);
            // 切换表单显示状态
            setShowPhoneForm(false);
        } catch (error) {
            setSubmitting(false);
            setVerificationCode('');
            setSubmitError(error.message);
            setSentCode(null);
            console.log(error);
        }
    };

    const handleVerificationCodeSubmit = async (values, { setSubmitting }) => {
        try {
            const data = {
                imsi: '000702735444444',
                countryCode: '34',
                phoneNumber: userPhoneNumber,
                verifCode: values.verificationCode,
            };
            const response = await axios.post('http://api-m-dev.testing.host:8082/jack.a/api1/1.2.0/verifySimCode', data);
            
            setSubmitting(false);
            setSubmitError(null);
            // 优先信任后端返回结果,兼容数字/字符串类型的resultCode
            if (response.data.resultCode === 200 || response.data.resultCode === '200') {
                alert('Success, your number is verified');
            } else {
                alert('Verification code is incorrect');
            }
        } catch (error) {
            setSubmitting(false);
            setSubmitError(error.message);
            alert('Wrong phone number or verification code');
        }
    };

    return (
        <div>
            <h1>Phone Service Subscription</h1>
            {submitError && <div className='error-message'>{submitError}</div>}
            
            {/* 通过状态控制手机号表单显示 */}
            {showPhoneForm && (
                <Formik initialValues={initialValues} validationSchema={validationSchema} onSubmit={handlePhoneNumberSubmit}>
                    {({ isSubmitting }) => (
                        <Form>
                            <label htmlFor='name'>Name:</label>
                            <Field id='name' name='name' type='text' />
                            <ErrorMessage name='name' />
                            
                            <label htmlFor='email'>Email:</label>
                            <Field id='email' name='email' type='email' />
                            <ErrorMessage name='email' />
                            
                            <label htmlFor='phoneNumber'>Phone Number:</label>
                            <Field id='phoneNumber' name='phoneNumber' type='tel' />
                            <ErrorMessage name='phoneNumber' />

                            <button type='submit' disabled={isSubmitting}>
                                {isSubmitting ? 'Submitting...' : 'Submit'}
                            </button>
                        </Form>
                    )}
                </Formik>
            )}

            {/* 通过状态控制验证码表单显示 */}
            {!showPhoneForm && (
                <Formik
                    initialValues={initialValues}
                    validationSchema={verificationSchema}
                    onSubmit={handleVerificationCodeSubmit}
                >
                    {({ isSubmitting }) => (
                        <Form>
                            <label htmlFor='verificationCode'>Verification Code:</label>
                            <Field id='verificationCode' name='verificationCode' type='text' />
                            <ErrorMessage name='verificationCode' />

                            <button type='submit' disabled={isSubmitting}>
                                {isSubmitting ? 'Submitting...' : 'Submit'}
                            </button>
                        </Form>
                    )}
                </Formik>
            )}
        </div>
    );
};

export default VerifyAxios;

关键修复点说明

  • 新增showPhoneForm状态,通过条件渲染实现表单切换,完全替代直接操作DOM的行为
  • 用userPhoneNumber状态存储用户输入的手机号,符合React单向数据流的状态管理规范
  • 验证码验证逻辑改为优先依赖后端返回的resultCode,移除前端冗余的sentCode对比,避免状态同步问题
  • 兼容后端返回resultCode为数字或字符串的情况,提升代码健壮性

内容的提问来源于stack exchange,提问作者Ancestral Craft

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 07:12:05