React+TypeScript集成react-credit-cards时onChange与onFocus报错如何解决
错误原因
- 核心错误是
react-credit-cards组件的number属性接收到了非字符串类型(比如undefined),调用内置的replace方法处理卡号时抛出异常。 - React的
useState不会自动合并对象状态,你原有代码中setState({ [name]: value })、setState({ focus: event.target.name })的写法会直接覆盖整个状态对象,导致card_no、cvc、exp等其他属性丢失变为undefined。 - 同时使用Formik管理表单状态和自定义
useState给信用卡组件传值时,两者状态不同步:自定义的changeHandler没有更新Formik的表单值,持卡人姓名、有效期、CVC三个输入框的状态也没有同步给stateList,导致信用卡组件无法拿到正确的渲染参数。 - 存在冗余且语法错误的独立
onChange方法,内部未定义name和event变量,属于无效代码。
修复方案
按照以下步骤修改代码即可解决问题:
- 修正状态更新逻辑,每次更新
stateList时先解构原有状态再覆盖新属性,避免其他属性丢失。 - 改造
changeHandler,同时更新Formik表单状态和stateList,保证两者数据一致。 - 给所有和信用卡信息相关的输入框绑定
onFocus事件,保证卡片聚焦状态正确切换。 - 删除冗余的无效
onChange方法。
修复后完整代码
import React, { useState, FocusEvent, ChangeEvent } from 'react' import Cards from 'react-credit-cards'; import 'react-credit-cards/es/styles-compiled.css' // 注意根据你的项目实际路径导入缺失的依赖:CustomerDashboardLayout、DashboardPageHeader、CreditCard、Link、Button、Card1、Formik、Box、Grid、TextField、checkoutSchema const PaymentMethodEditor = () => { const [stateList, setState] = useState(initialValues) const handleFormSubmit = async (values: any) => { console.log(values) } const focusHandler = (event: FocusEvent<HTMLInputElement>) => { // 函数式更新合并原有状态,避免覆盖其他属性 setState(prev => ({ ...prev, focus: event.target.name })) } // 柯里化改造,接收Formik的handleChange作为参数,同步更新两份状态 const changeHandler = (formikHandleChange: (e: ChangeEvent<HTMLInputElement>) => void) => (event: ChangeEvent<HTMLInputElement>) => { // 先更新Formik的表单状态 formikHandleChange(event); const { name, value } = event.target; // 同步更新给信用卡组件用的状态 setState(prev => ({ ...prev, [name]: value })) } return ( <CustomerDashboardLayout> <DashboardPageHeader icon={CreditCard} title={`${id === 'add' ? 'Add New' : 'Edit'} Payment Method`} button={ <Link href="/payment-methods"> <Button color="primary" sx={{ bgcolor: 'primary.light', px: '2rem' }}> Back to Payment Methods </Button> </Link> } /> <div id="PaymentForm"> <Cards cvc={stateList.cvc} expiry={stateList.exp} focused={stateList.focus} name={stateList.name} number={stateList.card_no} /> <Card1> <Formik initialValues={initialValues} validationSchema={checkoutSchema} onSubmit={handleFormSubmit} > {({ values, errors, touched, handleChange, handleBlur, handleSubmit }) => ( <form onSubmit={handleSubmit}> <Box mb={4}> <Grid container spacing={3}> <Grid item md={6} xs={12}> <TextField name="card_name" label="Card Name" fullWidth onBlur={handleBlur} onChange={changeHandler(handleChange)} value={values.card_name || ''} error={!!touched.card_name && !!errors.card_name} helperText={touched.card_name && errors.card_name} /> </Grid> <Grid item md={6} xs={12}> <TextField name="card_no" label="Card Number" fullWidth onBlur={handleBlur} onChange={changeHandler(handleChange)} onFocus={focusHandler} value={values.card_no || ''} error={!!touched.card_no && !!errors.card_no} helperText={touched.card_no && errors.card_no} /> </Grid> <Grid item md={6} xs={12}> <TextField name="name" label="Name on Card" fullWidth onBlur={handleBlur} onChange={changeHandler(handleChange)} onFocus={focusHandler} value={values.name || ''} error={!!touched.name && !!errors.name} helperText={touched.name && errors.name} /> </Grid> <Grid item md={6} xs={12}> <TextField name="exp" label="Exp. Date" fullWidth onBlur={handleBlur} onChange={changeHandler(handleChange)} onFocus={focusHandler} value={values.exp || ''} error={!!touched.exp && !!errors.exp} helperText={touched.exp && errors.exp} /> </Grid> <Grid item md={6} xs={12}> <TextField name="cvc" label="CVC" fullWidth onBlur={handleBlur} onChange={changeHandler(handleChange)} onFocus={focusHandler} value={values.cvc || ''} error={!!touched.cvc && !!errors.cvc} helperText={touched.cvc && errors.cvc} /> </Grid> </Grid> </Box> <Button type="submit" variant="contained" color="primary"> Save Changes </Button> </form> )} </Formik> </Card1> </div> </CustomerDashboardLayout> ) } const initialValues = { card_no: '', name: '', exp: '', cvc: '', focus: '', card_name: '', } export default PaymentMethodEditor
内容的提问来源于stack exchange,提问作者Yavuz
相关产品推荐
相关产品推荐

