React中Cannot read properties of null及验证码错误问题求助
问题分析与解决方案
问题概述
开发月度服务订阅的手机号验证功能时,提交手机号表单出现两个异常:
- 控制台报错
Cannot read properties of null (reading 'style'),指向handlePhoneNumberSubmit函数中操作DOM的代码行 - 弹出错误提示“Verification code is incorrect”,但后端测试验证码接口返回200 OK
错误原因分析
- DOM操作失败:
document.getElementById('phoneNumberForm')返回null,因为Formik组件的id属性不会被渲染到实际DOM节点上,直接通过id获取DOM元素的方法在React组件中不适用。 - 状态管理错误:直接修改
initialValues常量(initialValues.phoneNumber = values.phoneNumber)不符合React状态管理规范,会导致数据不一致。 - 验证码验证逻辑冗余:前端用
sentCode === values.verificationCode做二次验证,既多余又可能因为状态同步问题导致误判,应该优先信任后端返回的验证结果。 - 表单初始显示状态未控制:验证码表单默认显示,不符合“先提交手机号再验证”的流程逻辑。
修复步骤
- 使用React状态管理表单的显示/隐藏状态,替代直接操作DOM
- 用
useState存储用户输入的手机号,避免修改常量 - 调整验证码验证逻辑,仅依赖后端返回结果
- 通过条件渲染控制两个表单的显示
完整修复代码
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
相关产品推荐
相关产品推荐

