javascript - React | Como Passar FormikProps de um Componente para Outro Acima
Estou tentando passar values e props, que o formik precisa 1 componente acima. Estou usando vários pequenos componentes para alguns formulários, e estou passando eles em um componente complexo que precisa repassá-los para cada renderização individual quando chamado.
Basicamente todos os FormikProps. Aqui está um desses componentes.
import React, { Fragment } from 'react';
import debounce from 'debounce-promise';
import classNames from 'classnames';
import { Field, FormikProps, ErrorMessage } from 'formik';
import Asterisk from 'shared/common/components/element/Asterisk';
import { validateUsername } from '../../utils/index';
interface IValues {
username?: string;
email?: string;
}
export const InfoFields = (props: FormikProps<IValues>): JSX.Element => {
const debounceUsernameValidation = (): void => {
debounce(validateUsername, 500);
};
const { touched, errors } = props;
return (
<Fragment>
<div className="pb-2">
<label className="font-weight-bold" htmlFor="username">
Username <Asterisk />
</label>
<Field
validate={debounceUsernameValidation}
className={classNames('form-control', {
'is-invalid': errors.username && touched.username
})}
placeholder="Username (Required)"
autoComplete="username"
name="username"
type="text"
/>
<ErrorMessage name="username" component="div" className="text-danger" />
</div>
<div className="py-2">
<label className="font-weight-bold">Email</label>
<Field
className={classNames('form-control', {
'is-invalid': errors.email && touched.email
})}
autoComplete="email"
placeholder="Email"
name="email"
type="email"
/>
<ErrorMessage name="email" component="div" className="text-danger" />
</div>
</Fragment>
);
};
export default InfoFields;
E aqui estou chamando isso dentro do componente complexo:
render(): ReactNode {
const { mode } = this.props;
return (
<Formik
initialValues={this.getInitialValues()}
validationSchema={this.getValidationSchemas()}
onSubmit={this.handleSubmit}
validateOnBlur={false}
render={({ status, isSubmitting }) =>
(
<Form>
{status && (
<div className="mb-3 text-danger" data-test="user-form-error-message">
{status}
</div>
)}
{mode === ActionMode.ADD_USER && (
<Fragment>
<InfoFields /> // This is the component from above
</Fragment>
)}
<Button
className="btn btn-primary w-100 mt-5"
disabled={isSubmitting}
loading={isSubmitting}
type="submit"
>
{mode === ActionMode.ADD_USER && <span>CREATE USER</span>}
</Button>
</Form>
) as ReactNode
}
/>
);
}
Agora, quando eu chamo esse componente dentro de outro, recebo este erro:
Uncaught TypeError: Cannot read property 'username' of undefined. It refers to this line of code:
errors.username && touched.username and this
errors.email && touched.email
interface IProps {
doSubmit(service: object, values: object): LensesHttpResponse<string>;
onSave(values: { username?: string; email?: string; password?: string; group?: string }): void;
notify(config: object): void;
mode: ActionMode;
user: IUser;
}
interface IValues extends FormikValues {
username?: string;
email?: string;
password?: string;
group?: string;
}
Eu preciso de uma maneira de passar as props username, email e o resto para cada componente individual. O problema é que o componente é chamado 2 renderizações abaixo, então ele não tem acesso a elas
Estou meio perdido aqui. Alguém pode me ajudar? Obrigado!!
Resposta #1
Você na verdade não está passando props para InfoFields, embora você tenha escrito esse componente para aceitar FormikProps<IValues>. Ou você pode passar os props do Formik assim:
<Formik
render={formikProps => (
<Form>
// Other Code.
<InfoFields {...formikProps} />
// Other Code.
</Form>
)}
/>
Ou (minha preferência pessoal), remova as props de InfoField e use Field como uma render prop, por exemplo:
<Field
name="username"
validate={debounceUsernameValidation}
>
{({ field, form }: FieldProps) => (
<Fragment>
<input
{...field}
className={classNames('form-control', {
'is-invalid': form.errors[field.name] &&
form.touched[field.name]
})}
placeholder="Username (Required)"
type="text"
/>
<ErrorMessage
name={field.name}
component="div"
className="text-danger"
/>
</Fragment>
)}
</Field>
Com a propriedade de renderização do campo, você pode acessar os valores do formulário em componentes mais profundamente aninhados sem precisar passar props por toda parte.
