I have the following code. The checkbox is for confirming that the user has read Terms and Conditions. Im not sure how to add validation for this on the front end. i.e. If the user clicks submit without checking this box, I want to highlight this text in some way that lets the user know to agree to Terms.
<Field
label='Terms'
name="terms-and-conditions"
type="checkbox"
component={this.renderField}
submissionErrors={this.state.submissionErrors}
/>
You can create a custom component for the Field component. In the custom component you will receive error prop so you can process with additional styling, error messages, etc. If you want to validate on submit, pass onSubmit prop to the decorated component.
Live example: https://codesandbox.io/s/207mj8k3py
Create a custom component with the error logic:
const Checkbox = ({ input, meta: { touched, error } }) => (
<div style={{ border: touched && error ? "1px solid red" : "none" }}>
<input type="checkbox" {...input} />
<label>Terms and conditions</label>
</div>
)
Set Field's component prop to Checkbox:
const SimpleForm = ({ handleSubmit, onSubmit }) => (
<form onSubmit={handleSubmit(onSubmit)}>
<Field
name="termsAndConditions"
component={Checkbox}
/>
<div>
<button type="submit">Submit</button>
</div>
</form>
)
In onSubmit function, check if the checkbox is checked, if not - throw SubmissionError with the name of the Field:
const onSubmit = values => {
if (!values.termsAndConditions) {
throw new SubmissionError({
termsAndConditions: 'Field required'
})
}
console.log('Form submitted')
}
Decorate the form:
export default reduxForm({
form: "simple",
onSubmit
})(SimpleForm)
Related
In the project I'm working, I have wrapped the input around a form, and the idea is when the user types something and clicks enter, the data is logged. But the issue is I'm not getting such logs.
Here is my code :
Index.tsx
const onFinish = (values: any) => {
console.log("yoyo");
console.log(values);
};
<Form name="control-ref" onMouseEnter={onFinish}>
<Form.Item name="note" rules={[{ required: true }]}>
<InputField
variant="search"
placeholder="Search"
addonAfter={selectAfter}
className="search-field"
/>
</Form.Item>
</Form>
The InputField with the specified variant returns this components
const SearchField: React.FC<IInputFieldProps> = (props) => {
const { placeholder } = props;
return (
<Input
className="search-field"
placeholder={placeholder || 'Search'}
{...props}
/>
);
};
Please help
Log the data when the form is submitted. Forms can be submitted by pressing enter.
<Form name="control-ref" onFinish={(values: any) => {
console.log("yoyo");
console.log(values);
}}>
{...}
</Form>
Edit: updated submission callback according to ant design react docs
Following the Radio Group Example in the Formik docs and using Bootstrap 5 I've tried to create a boolean radio component that gets passed in a name and label. However, on render with the initialValues:
<Formik
initialValues={{
foo: false,
bar: false,
}}>
the no is selected because the initial value is set to false but whenever yes is chosen the radio will not fill in, component:
import React from 'react'
import PropTypes from 'prop-types'
import { Field } from 'formik'
const RadioBool = ({ name, label }) => {
const yesId = `${name}-yes`
const noId = `${name}-no`
return (
<>
<p id={`${name}-group-label`}>{label}:</p>
<div className="form-group mb-4" role="group" aria-labelledby={`${name}-group-label`}>
<div className="form-check">
<label htmlFor={yesId}>Yes</label>
<Field id={yesId} className="form-check-input" type="radio" name={name} value={true} />
</div>
<div className="form-check">
<label htmlFor={noId}>No</label>
<Field id={noId} className="form-check-input" type="radio" name={name} value={false} />
</div>
</div>
</>
)
}
RadioBool.propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
}
export default RadioBool
but if checked is added to Yes, example:
<Field id={yesId} className="form-check-input" type="radio" name={name} value={true} checked />
the radio group works and I'm not sure why.
tests:
<RadioBool name="foo" label="Look at foo?" />
<RadioBool name="bar" label="Look at bar?" />
Research
How to add Radio Button in Formik Validations Reactjs?
How to create Radio buttons with Formik?
How to make use of Radio Group with useFormik Hook
how do you use onChange property on react formik radio field
formik radio button validation issues
Why does my boolean radio component not work in Formik unless checked is used on one <Field />?
The problem is described here Radio values must be strings, can't be numbers or booleans and as I see it's not solved 'from the box' yet.
In my TypeScript project I've created custom component. May it would be helpfull for you:
import React, { FC } from 'react';
type Props = {
name: string
value: boolean
checked: boolean
setValues: (values: React.SetStateAction<any>, shouldValidate?: boolean) => void;
}
const RadioButtonBooleanField: FC<Props> = ({name, value, checked, setValues}) => {
return (
<input type='radio' className="form-check-input" name={name} value={String(value)} checked={checked}
onChange={(e: any) => {
setValues((prevValues: any) => ({
...prevValues,
[e.target.name]: e.target.value === "true"
}))
}
}
/>
);
};
It can be used like this inside Formik component:
{({ errors, touched, values, setValues }) => (
<RadioButtonBooleanField name="IsExtendedView" value={false} checked={!values.IsExtendedView} setValues={setValues}/>
)}
I have a form component that include multiple TextFields and each one have different validation and error. Assume my TextField had custom validation prop that will take some conditions and show validation either onChange or onBlur for each TextField. I also have custom error prop that display message without condition.
const Address = ({
onChange,
required,
error,
value = {},
validation = undefined,
}: AddressProps) => {
const validator = {
validationAddress: (value: string) => (value.length === 0) ? 'Address is required' : '',
validationCity: (value: string) => (value.length === 0) ? 'City is required' : '',
}
const handleChange = (event) => {
...
}
return (
<React.Fragment>
<TextField
id="address"
label="address"
type="text"
required={required}
onChange={handleChange}
value={value.address}
validationEvent="change"
validation={validator.validationAddress}
/>
<TextField
id="city"
label="city"
type="text"
required={required}
onChange={handleChange}
value={value.city}
validationEvent="change"
validation={validator.validationCity}
/>
</React.Fragment>
export default Address;
After that I will implement my Address component in an App with submit button.
const App = () => {
const handleSubmit = () => {
...
}
return (
<Address />
<button type='submit' onClick={handleSubmit}>
Submit
</button>
)
}
export default App;
I saw many examples that input and form put together with submit button but I am struggling with handle them separately this way. My task is need to handle validation in Address component but have submit button as an optional option outside the Address component. My goal is validation message should not show until Submit button is clicked.
Try this:
move validator to parent component
pass validatorMessage useState var to Address component
call the validator by the click of submit button
Now the message is managed in the parent component and you control when the submit button displays it.
I would suggest to consider a different approach: there is no need to manually implement validation, we can use the browser built-in validation feature:
https://medium.com/p/491327f985d0
Essentially I use standard HTML mark-up and properties.
Implementation example:
export default function App() {
const onSubmit = (data: FormData) => {
console.log(Object.fromEntries(data.entries()));
};
return (
<div className="App">
<Form onSubmit={onSubmit}>
<h2>A simple form</h2>
<TextInput label="Name:" id="name" name="name" required />
<TextInput
label="Email:"
id="email"
type="email"
name="email"
required
/>
<TextInput label="Address:" id="address" name="address" />
<TextInput
label="Tel:"
id="tel"
name="tel"
type="tel"
pattern="((\+44(\s\(0\)\s|\s0\s|\s)?)|0)7\d{3}(\s)?\d{6}" // don't rely on this
/>
<button>Submit</button>
</Form>
</div>
);
}
The Form component itself is very simple:
<form
action={action}
onSubmit={handleSubmit}
noValidate
className={style.form}
>
<div className={style.wrapper}>{children}</div>
</form>
It sets the noValidate attribute to prevent the default error notification to pop up.
The input boxes are wrapped in the form element.
The form element has an onSubmit event handler: it gets triggered when user clicks on the submit button OR when the user hit return using the keyboard.
At that point we use e.preventDefault(); to prevent default form submission and handle it manually.
I would like to use useEffect to rerender a field if values of formik has changed. But it doesn't work..
Actually I created a little example to show my problem.
Here I have one field 'title', if this field has changed (when we write in it) it should call useEffect and print 'update!' but it doesn't!!
const FormikWidgetConfigurator = (props) => {
useEffect(() => {
// doesn't work if values has changed
console.log('update!')
}, [props.values]);
return (
<Form onSubmit={ props.handleSubmit } noValidate>
<Form.Group className='py-3' >
<Col md='6' style={{ padding: '0px' }}>
<Form.Group controlId='title'>
<Form.Control
type='text'
value={ props.values.title }
onChange={props.handleChange}
/>
</Form.Group>
</Col>
</Form.Group>
</Form>
)
}
const WidgetConfigurator = withFormik({
mapPropsToValues(props) {
return {
title: 'No Title'
};
},
validationSchema: props => Yup.object().shape({title: Yup.string()}),
handleSubmit(values, { setSubmitting }) {// handle submit}
})(FormikWidgetConfigurator);
export default WidgetConfigurator;
EDIT: Actually it works as expected. (i didn't change anything)
Thanks!
Using vanilla Formik your approach works.
My guess is that the issue is in your custom components, Form.Control, or Form.Group
It's not a great solution, but a hack I found is to have an invisible button inside a Formik form; it has access to all of Formik's attributes and will do whatever logic is needed in its onClick. Then from a useEffect you can simulate the click of that button via document.getElementById("..").click().
// Style it to be invisible
<Button id="testButton" type="button" onClick={() => {
setFieldValue('test', '123');
setTouched({});
// etc. any Formik action
}}>
</Button>
useEffect:
useEffect(() => {
document.getElementById("testButton").click(); // Simulate click
}, [dependency);
What are use cases for handleBlur? Since you can access touched fields through the touched object.
From Formik Docs:
handleBlur: (e: any) => void
onBlur event handler. Useful for when you need to track whether an input has been touched or not. This should be passed to <input onBlur={handleBlur} ... />
handleBlur is how touched is actually updated (or at least one of the ways). If your input isn't passed an onBlur handler connecting to Formik's state, the touched state won't be updated.
Here is a codesandbox illustrating the behavior: https://codesandbox.io/s/magical-gates-4cq5k?file=/src/App.js
If you blur the first input, touched is updated. If you blur the second input, touched is not updated. The third input uses useField, which results in an onBlur field being passed to my input automatically. This is typically how I like to work with Formik (or pretty much any other react form library), since it cuts down on the boilerplate necessary to connect form fields to the state of the form.
import React from "react";
import { Formik, Form, useField } from "formik";
import "./styles.css";
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Formik
initialValues={{ usingUseField: "", withBlur: "", withoutBlur: "" }}
>
{({ handleChange, handleBlur, values, touched }) => {
return (
<Form>
{JSON.stringify(touched)}
<div>
<label>
With Blur
<input
onBlur={handleBlur("withBlur")}
onChange={handleChange("withBlur")}
value={values.withBlur}
/>
</label>
</div>
<div>
<label>
Without Blur
<input
onChange={handleChange("withoutBlur")}
value={values.withoutBlur}
/>
</label>
</div>
<InputUsingUseField name="usingUseField" label="Using useField" />
</Form>
);
}}
</Formik>
</div>
);
}
function InputUsingUseField({ label, name }) {
const [props] = useField(name);
return (
<div>
<label>
{label}
<input {...props} />
</label>
</div>
);
}