如何将Formik的field属性传入Storybook Story并解决未定义报错
Formik自定义TextField组件的Storybook报错:无法解构'field'的'name'属性,因为它未定义
自定义TextField组件代码
import { memo } from 'react'; import { OutlinedTextFieldProps } from '@mui/material/TextField'; import { InputAdornment, TextField as MuiTextField } from '@mui/material'; import { FieldProps } from 'formik'; interface TextFieldPropsInterface extends OutlinedTextFieldProps, FieldProps { validationErrors?: Record<string, string[]> | undefined; loading?: boolean; endAdornmentText?: string; } function TextField({ loading, validationErrors, field, form, endAdornmentText, ...TextFieldProps }: TextFieldPropsInterface) { const { name } = field; const { touched, errors } = form; const { helperText } = TextFieldProps; const hasError = touched[name] && Boolean(errors[name]); const errorMessage = errors && errors[name]; return ( <MuiTextField {...TextFieldProps} error={hasError} helperText={hasError ? errorMessage : helperText} disabled={loading || TextFieldProps?.disabled} InputProps={{ endAdornment: endAdornmentText ? ( <InputAdornment position='end'>{endAdornmentText}</InputAdornment> ) : null, }} {...field} /> ); } export default memo(TextField);
Storybook代码
import { ComponentStory, ComponentMeta, storiesOf } from '@storybook/react'; import { withFormik } from '@bbbtech/storybook-formik'; import FormikTextField from '.'; export default { component: FormikTextField, decorators: [withFormik], } as unknown as ComponentMeta<typeof FormikTextField>; const Template: ComponentStory<typeof FormikTextField> = (args) => ( <FormikTextField {...args} /> ); export const Default = Template.bind({}); Default.args = { loading: false, validationErrors: undefined, endAdornmentText: 'End adornment', };
报错信息
TypeError Cannot destructure property 'name' of 'field' as it is undefined. Call Stack TextField main.iframe.bundle.js:1497:5 renderWithHooks vendors-node_modules_bbbtech_storybook-formik_dist_esm_index_js-node_modules_mui_icons-materi-41c387.iframe.bundle.js:116439:18 mountIndeterminateComponent vendors-node_modules_bbbtech_storybook-formik_dist_esm_index_js-node_modules_mui_icons-materi-41c387.iframe.bundle.js:119265:13 beginWork vendors-node_modules_bbbtech_storybook-formik_dist_esm_index_js-node_modules_mui_icons-materi-41c387.iframe.bundle.js:120503:16 HTMLUnknownElement.callCallback vendors-node_modules_bbbtech_storybook-formik_dist_esm_index_js-node_modules_mui_icons-materi-41c387.iframe.bundle.js:105399:14 Object.invokeGuardedCallbackDev vendors-node_modules_bbbtech_storybook-formik_dist_esm_index_js-node_modules_mui_icons-materi-41c387.iframe.bundle.js:105448:16 invokeGuardedCallback vendors-node_modules_bbbtech_storybook-formik_dist_esm_index_js-node_modules_mui_icons-materi-41c387.iframe.bundle.js:105510:31 beginWork$1 vendors-node_modules_bbbtech_storybook-formik_dist_esm_index_js-node_modules_mui_icons-materi-41c387.iframe.bundle.js:125413:7 performUnitOfWork vendors-node_modules_bbbtech_storybook-formik_dist_esm_index_js-node_modules_mui_icons-materi-41c387.iframe.bundle.js:124225:12 workLoopSync vendors-node_modules_bbbtech_storybook-formik_dist_esm_index_js-node_modules_mui_icons-materi-41c387.iframe.bundle.js:124156:5
原因分析
自定义组件依赖Formik的FieldProps,必须由Formik的Field组件或withFormik装饰器注入field和form对象。当前报错是因为field属性未被正确传递,导致解构field.name时失败。
解决方案
方案1:完善Storybook的Formik配置,传递必要字段信息
@bbbtech/storybook-formik的withFormik装饰器需配置表单初始值,同时给组件指定name属性,修改后的Story代码如下:
import { ComponentStory, ComponentMeta } from '@storybook/react'; import { withFormik } from '@bbbtech/storybook-formik'; import FormikTextField from '.'; export default { component: FormikTextField, decorators: [withFormik], parameters: { formik: { initialValues: { username: '', // 定义表单初始值,字段名需和组件name对应 }, }, }, } as unknown as ComponentMeta<typeof FormikTextField>; const Template: ComponentStory<typeof FormikTextField> = (args) => ( <FormikTextField {...args} /> ); export const Default = Template.bind({}); Default.args = { loading: false, validationErrors: undefined, endAdornmentText: 'End adornment', name: 'username', // 指定字段名,和initialValues中的key对应 };
方案2:给组件添加容错处理,防止field未定义
在组件中给field和form设置默认值,避免未注入时报错:
function TextField({ loading, validationErrors, field = { name: '' }, // 添加默认值 form = { touched: {}, errors: {} }, // 添加默认值 endAdornmentText, ...TextFieldProps }: TextFieldPropsInterface) { const { name } = field; const { touched, errors } = form; const { helperText } = TextFieldProps; const hasError = touched[name] && Boolean(errors[name]); const errorMessage = errors && errors[name]; return ( <MuiTextField {...TextFieldProps} error={hasError} helperText={hasError ? errorMessage : helperText} disabled={loading || TextFieldProps?.disabled} InputProps={{ endAdornment: endAdornmentText ? ( <InputAdornment position='end'>{endAdornmentText}</InputAdornment> ) : null, }} {...field} /> ); }
方案3:改用Formik的Field组件包裹TextField
如果withFormik装饰器无法正确注入属性,可直接在Story模板中用Formik的Field组件包裹自定义TextField:
import { ComponentStory, ComponentMeta } from '@storybook/react'; import { Formik, Form, Field } from 'formik'; import FormikTextField from '.'; export default { component: FormikTextField, } as unknown as ComponentMeta<typeof FormikTextField>; const Template: ComponentStory<typeof FormikTextField> = (args) => ( <Formik initialValues={{ username: '' }} onSubmit={() => {}} > <Form> <Field name="username" component={FormikTextField} {...args} /> </Form> </Formik> ); export const Default = Template.bind({}); Default.args = { loading: false, validationErrors: undefined, endAdornmentText: 'End adornment', };
内容的提问来源于stack exchange,提问作者Atlante Avila
相关产品推荐
相关产品推荐

