Reputation: 356
using Reactjs with typescript
I want to pass the useFormik hook to the component props.
The reason for this is to reduce unnecessary lines and increase reuse.
My current code
...
const formik = useFormik({
initialValues: { userName: ''},
validationSchema,
onSubmit: (values) => {}
})
return (
<Form>
{/* A place to make a component. */}
<Text
id="userName"
fullWidth
label="Name"
defaultValue={formik.values.userName}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.userName && Boolean(formik.errors.userName)}
helperText={formik.touched.userName && formik.errors.userName}
>
{/* A place to make a component. */}
</Form>
)
Custom component, which is the main point of the question.
interface props {
id: string;
formik : what, // How do I deliver the prop here?
}
const TextFieldCustom = ({ id, formik }: props) => {
return (
<Text
id={id}
fullWidth
label={id}
defaultValue={formik.values.userName}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.userName && Boolean(formik.errors.userName)}
helperText={formik.touched.userName && formik.errors.userName}
>
);
};
My code completed because of your answer.
...
const formik = useFormik({
initialValues: { userName: ''},
validationSchema,
onSubmit: (values) => {}
})
return (
<Form>
{/* A place to make a component. */}
<TextFieldCustom id="username" formik={formik}/>
{/* A place to make a component. */}
</Form>
)
I want your good solution.
Upvotes: 1
Views: 7196
Reputation: 545
If you're in a Next.js SSR environment, formik will initially return undefined before initialization so you might need to add a guard to help silent this error.
type FormProps = {
formik: FormikProps<YourInterface>;
}
function Form({formik}: FormProps) {
if(!formik) return null // Guard to prevent errors
// You can use formik however you want from here.
console.log(formik)
return (
<form onSubmit={formik.handleSubmit}>
<label>
<input
name="firstName"
value={formik.values.firstname}
onChange={formik.handleChange}
/>
</label>
</form>
)
}
Another way you can achieve this is by wrapping the component with the useFormik <Form>
component and then get the form context using useFormikContext
hook. Here's how that would work.
import { Form, Formik } from "formik";
function App() {
return (
<Formik initialValues={initialValues} onSubmit={(values) => console.log(values)}>
<Form>
<CustomComponent />
</Form>
</div>
)
}
Now the form values should be accessible via the useFormik context.
// CustomComponent.tsx
import { useFormikContext } from "formik";
function CustomComponent() {
const formikContext = useFormikContext<YourInterface>();
// Next.js guard to prevent initialization errors
if(!formikContext) return;
const { values, handleChange } = formikContext;
return (
<div>
<label>
<input
name="firstName"
value={values.firstName}
onChange={handleChange}
/>
</label>
</form>
)
}
Upvotes: 1
Reputation: 11
To achieve what you want, you can take the Manjunath answer as base and make these changes.
The parent component would be like this:
const { initial, validators } = schemaSignUpForm//import your yup validator;
...
const formik = useFormik<YourInterface>({
initialValues: initial, // you can destructure your interface object here. Not obliged to the schema above
validationSchema,
onSubmit: (values) => {}
})
And the new component which receives the formik props would be like this:
interface props {
id: string;
formik : FormikProps<YourInterface>
}
const TextFieldCustom = ({ id, formik }: props) => {
return (
<Text
id={id}
fullWidth
label={id}
defaultValue={formik.values.userName}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.userName &&
Boolean(formik.errors.userName)}
helperText={formik.touched.userName && formik.errors.userName}
>
);
};
Upvotes: 1
Reputation: 51
You can use FormikProps
import { FormikProps } from 'formik';
interface props {
id: string;
formik : FormikProps<{username: string}>
}
const TextFieldCustom = ({ id, formik }: props) => {
return (
<Text
id={id}
fullWidth
label={id}
defaultValue={formik.values.userName}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.userName && Boolean(formik.errors.userName)}
helperText={formik.touched.userName && formik.errors.userName}
>
);
};
Upvotes: 1
Reputation: 720
If you want to access formik helpers from the child component you can use useFormikContext. I think it's easier.
From your code I would also recommend to use Formik component as parent of Form component as it needs it but that could be another thing.
Here's what I would do (note that I've substituted Form component in favor of form html tag):
Parent component:
export interface IFormData {
username: string;
}
const validationSchema = Yup.object().shape({
userName: Yup.string()
.required("Username required"),
});
const ParentComponent = () => {
const formikConfig = useFormik({
initialValues: { userName: "" },
validationSchema,
onSubmit: (values) => {},
});
return (
<form onSubmit={formikConfig.handleSubmit}>
<TextCompo id="id" />
</form>
);
};
export default ParentComponent;
Children component:
interface ITextCompoProps {
id: string;
}
const TextFieldCustom = (props: ITextCompoProps) => {
const { id } = props;
const context = useFormikContext<IFormData>();
return (
<Text
id={id}
fullWidth
label={id}
defaultValue={context.values.userName}
onChange={context.handleChange}
onBlur={context.handleBlur}
error={!!(context.errors.username && context.touched.username)}
helperText={context.touched.username && context.errors.username}
/>
);
};
export default TextFieldCustom;
Upvotes: 6