Reset input on uncheck checkbox (ReactJS + Formik) - reactjs

I made this component to create a field if a checkbox is checked, but how can I reset this field value if I write something and then uncheck?
const InputCheckbox = ({name, size}) => {
const [checkAmount, setCheckAmount] = useState(false);
return(
<div>
<label htmlFor={size}>
<Field type="checkbox" name={name} value={size} onClick={()=> {checkAmount === false ? setCheckAmount(true) : setCheckAmount(false)}} />
{size}
</label>
{checkAmount === false ? null : <div className="form-control-amount">
<label htmlFor={`sizeamount.${size}`}>{size}</label>
<Field className="form-control-amount" type="number" name={`sizeamount.${size}`} />
</div>}
</div>
)
}

You can use Formik's setFieldValue function to reset the field when the checkbox is unchecked.
Your component:
const InputCheckbox = ({name, size, setFieldValue }) => {
const [checkAmount, setCheckAmount] = useState(false);
return(
<div>
<label htmlFor={size}>
<Field type="checkbox" name={name} checked={checkAmount} value={size} onClick={()=> {checkAmount === false ? setCheckAmount(true) : setCheckAmount(false); setFieldValue(`sizeamount.${size}`, '')}} />
{size}
</label>
{checkAmount === false ? null : <div className="form-control-amount">
<label htmlFor={`sizeamount.${size}`}>{size}</label>
<Field className="form-control-amount" type="number" name={`sizeamount.${size}`} />
</div>}
</div>
)
}
Usage:
<Formik
initialValues={{
check: true
}}
onSubmit={(values, actions) => {
alert('Form has been submitted');
actions.setSubmitting(false);
}}
>
{({setFieldValue}) => (
<Form>
<InputCheckbox name="check" size={10} setFieldValue ={setFieldValue} />
<button type="submit">Submit</button>
</Form>
)}
</Formik>
Reference: https://formik.org/docs/api/formik

You can
<button
class="btn btn-primary pull-right"
type="reset"
onClick={(e) => {
e.preventDefault();
props.resetForm()
}}
>Eliminar filtros</button>

Related

Warning: "unstable_flushDiscreteUpdates" on formik custom input

I am trying to create a custom input, so I can use it in my formik form, and I get this warning.
For the custom input I used this example: https://formik.org/docs/api/useField
P.S before adding this custom input, there wasn't any error.
My code:
const Input = (props) => {
const [meta] = useField(props);
return (
<div className={classes.control}>
<label htmlFor={props.id || props.name}>{props.label}</label>
<Field name={props.name} {...props} />
{meta.touched && meta.error ? <div className="error">{meta.error}</div> : null}
</div>
);
};
export default Input;
const RegistrationForm = () => {
const { isLoading, t, submitHandlerRegister, registrationValues } = useAuth();
return (
<Formik
initialValues={registrationValues}
validationSchema={RegisterValidationSchema}
onSubmit={submitHandlerRegister}
enableReinitialize={true}
>
<Form>
<Input
label={t("Authentication.Email")}
name="email"
type="email"
placeholder={t("Authentication.Email")}
></Input>
<Input
label={t("Authentication.Password")}
name="password"
type="password"
placeholder={t("Authentication.Password")}
></Input>
<div className={classes.actions}>
<button type="submit" disabled={isLoading}>
{t("Authentication.CreateAccount")}
</button>
</div>
</Form>
</Formik>
);
};

React Formik: My custom onBlur override default touched behavior and not displaying my ErrorMessage

I just started learning to code 2 months ago, this week I tried to work with Formik and had this issue where I can't display my ErrorMessage after I added onBlur on the field, before added it, was working normally.
Also, I didn't find another way to work with onBlur, that's why I code it on the field.
I appreciate it if have a hand.
import { Formik, Field, Form, ErrorMessage } from "formik";
import Schema from "./Schema";
import { Container, Row, Col } from "react-bootstrap";
import { useState } from "react";
import Result from "./Result";
import { BsEyeSlashFill } from "react-icons/bs";
const Register = () => {
const [data, setData] = useState(false);
const [showPassword, setShowPassword] = useState(false)
const [showConfirmPassword, setConfirmShowPassword] = useState(false)
const [checkMarkName, setCheckMarkName] = useState(null)
const [checkMarkSurname, setCheckMarkSurname] = useState(false)
const [checkMarkEmail, setCheckMarkEmail] = useState(false)
const [checkMarkPass, setCheckMarkPass] = useState(false)
const [checkMarkConfirmPass, setCheckMarkConfirmPass] = useState(false)
const hidePassword = () => {
setShowPassword(!showPassword)
}
const hideConfirmPassword = () => {
setConfirmShowPassword(!showConfirmPassword)
}
function onSubmit(values, actions) {
setData(values)
}
return (
<Formik
validationSchema={Schema}
onSubmit={onSubmit}
validateOnMount
initialValues={{
name: "",
surname: "",
email: "",
password: "",
confirmPassword: "",
}}
>
{(props) => (
<Container>
<Row className="justify-content-center mt-3" >
<Col className="text-center d-flex justify-content-center" md={8}>
<Form className="mt-5 div-form w-100">
{console.log(props)}
<div className="errors mt-3">
<label>Nome</label>
<Field onBlur={() => {
if(!props.errors.name)(
setCheckMarkName(true)
)
}} name="name" className={checkMarkName ? "form-control form-control-green" : "form-control"} type="text" />
<div><ErrorMessage name="name" /></div>
</div>
<div className="errors mt-3">
<label>Surname</label>
<Field onBlur={() => {
if(!props.errors.surname)(
setCheckMarkSurname(true)
)
}} name="surname" className={checkMarkSurname ? "form-control form-control-green" : "form-control"} type="text" />
<div><ErrorMessage name="surname" /></div>
</div>
<div className="errors mt-3">
<label>Email</label>
<Field onBlur={() => {
if(!props.errors.email)(
setCheckMarkEmail(true)
)
}} name="email" className={checkMarkEmail ? "form-control form-control-green" : "form-control"} type="email" />
<div><ErrorMessage name="email" /></div>
</div>
<div className="errors mt-3 position-relative">
<label>Password</label>
<Field onBlur={() => {
if(!props.errors.password)(
setCheckMarkPass(true)
)
}}
name="password"
className={checkMarkPass ? "form-control form-control-green" : "form-control"}
type={showPassword ? 'text' : 'password'}
></Field>
<span className="position-relative eyes text-white"><BsEyeSlashFill onClick={() => hidePassword()} /></span>
<div className="position-relative error-msg"><ErrorMessage name="password" /></div>
</div>
<div className="errors mb-4 position-relative">
<label>Confirm Password</label>
<Field onBlur={() => {
if(!props.errors.confirmPassword)(
setCheckMarkConfirmPass(true)
)
}}
name="confirmPassword"
className={checkMarkConfirmPass ? "form-control form-control-green" : "form-control"}
type={showConfirmPassword ? 'text' : 'password'}
/>
<span className="position-relative eyesConfirm text-white"><BsEyeSlashFill onClick={() => hideConfirmPassword()} /></span>
<div className="position-relative error-msg"><ErrorMessage name="confirmPassword" /></div>
</div>
<button
className="btn btn-primary mb-3"
disabled={!props.isValid}
type="submit"
>
Enviar
</button>
<button
className="btn btn-primary mb-3 ml-3"
type="button"
onClick={() => setData(false)}
>
Reset
</button>
</Form>
</Col>
{
data &&
<Result data={data}/>
}
</Row>
</Container>
)}
</Formik>
);
};
export default Register;

How to get values from react FieldArray in formik form with other fields?

I have created a Formik form that contains a field array, form and fieldArray is in two separate classes as separate components.
My form:
<Formik onSubmit = {(values, { setSubmitting }) => { setSubmitting(false);}}
enableReinitialize>
{({handleSubmit, errors})=> (
<Form onSubmit= { handleSubmit }>
<Form.Group as= { Row } controlId= "cpFormGroupTitle" className="required">
<Form.Label className="post-create-label" column sm={ 2 } >
Title
</Form.Label>
<Col sm={ 10 }>
<Field name="title" component={ renderTextField } type="text"
isinvalid={ !!errors.title ? "true": "false" }
placeholder="Title *" />
</Col>
</Form.Group>
<Form.Group as= { Row } controlId= "cpFrmGroupShortDesc" className="required">
<Form.Label className="post-create-label" column sm={ 2 } >
Short Description
</Form.Label>
<Col sm={ 10 }>
<Field name="short-desc" component={ renderTextArea } type="text"
isinvalid={ !!errors.shortDescription ? "true": "false" }
placeholder="Short Description *" />
</Col>
</Form.Group>
<Form.Group as= { Row } controlId= "cpFormGroupFeatures">
<Form.Label className="post-create-label" column sm={ 2 }>
Features
</Form.Label>
<Col sm={ 10 }>
<TextFieldArray initialValues={{ features: [] } } name="features"/>
</Col>
</Form.Group>
<Form.Group as={ Row }>
<Col sm= { { span: 2, offset:2 } }>
<Button type="submit" variant="primary">Submit</Button>
</Col>
<Col sm={ 2 }>
<Button variant="secondary">Save as draft</Button>
</Col>
</Form.Group>
</Form>
)}
</Formik>
Here, <TextFieldArray> is field array , I need to get values from field array when form is submitted.
TextFieldArray:
export const TextFieldArray = (props) => (
<React.Fragment>
<Formik initialValues= { props.initialValues } render={({ values }) => (
<Form>
<FieldArray name= { props.name } render={arrayHelper => (
<div>
{ values[props.name] && values[props.name].length > 0 ?
(
values[props.name].map((item, index) => (
<div key={index}>
<Form.Group as= { Row }>
<div className="col-md-8">
<Field name={`${props.name}.${index}`}
className="form-control"/>
</div>
<div className="col-md-2">
<Button type="button" variant="outline-secondary"
onClick={() => arrayHelper.remove(index)}>
Remove
</Button>
</div>
<div className="col-md-2">
<Button type="button" variant="outline-secondary"
onClick={() => arrayHelper.insert(index, '')}>
Add
</Button>
</div>
</Form.Group>
</div>
))
) : (
<Button type="button" variant="outline-secondary"
onClick={() => arrayHelper.push('')} >
{`Add ${ props.name }`}
</Button>
)
}
</div>
)} />
</Form>
)} />
</React.Fragment>
);
I'm a beginner to ReactJS, so someone help me please, that will be huge help from you all.
Thanks.
To have field array be part of same form as other fields, only have one <Formik> and one <Form>. Then make initialValues on Formik that describes all the fields:
<Formik
initialValues={{ friends: someFriends, random: randomText }}
As seen in the following code from Formik FieldArray docs, with another form field added that is not part of the array:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { Formik, Form, Field, useField, FieldArray } from "formik";
const someFriends = ["jared", "ian", "brent"];
const randomText = "Four score and seven years ago...";
function MyTextInput({ label, ...props }) {
// useField() returns [formik.getFieldProps(), formik.getFieldMeta()]
// which we can spread on <input> and alse replace ErrorMessage entirely.
const [field, meta] = useField(props);
return (
<>
<label
htmlFor={props.id || props.name}
css={{ backgroundColor: props.backgroundColor }}
>
{label}
</label>
<input className="text-input" {...field} type="text" {...props} />
{meta.touched && meta.error ? (
<div className="error">{meta.error}</div>
) : null}
</>
);
}
// Here is an example of a form with an editable list.
// Next to each input are buttons for insert and remove.
// If the list is empty, there is a button to add an item.
export const FriendList = () => (
<div>
<h1>Friend List</h1>
<Formik
initialValues={{ friends: someFriends, random: randomText }}
onSubmit={values =>
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
}, 500)
}
render={({ values }) => (
<Form>
<MyTextInput label="Random comment" name="random" />
<FieldArray
name="friends"
render={arrayHelpers => (
<div>
{values.friends &&
values.friends.length > 0 &&
values.friends.map((friend, index) => (
<div key={index}>
<Field name={`friends.${index}`} />
<button
type="button"
onClick={() => arrayHelpers.remove(index)} // remove a friend from the list
>
-
</button>
<button
type="button"
onClick={() => arrayHelpers.insert(index, "")} // insert an empty string at a position
>
+
</button>
</div>
))}
{/* Add a new empty item at the end of the list */}
<button type="button" onClick={() => arrayHelpers.push("")}>
Add Friend
</button>
<div>
<button type="submit">Submit</button>
</div>
</div>
)}
/>
</Form>
)}
/>
</div>
);
ReactDOM.render(<FriendList />, document.getElementById("root"));
Code in codesandbox.
I don't think you need to create second form for child component.
You need to just pass the values from the parent to the TextFieldArray
<TextFieldArray values={values.myArr} name="features"/>
And the child component just receive the values and render them (as if it was in the parent component)
export const TextFieldArray = (props) => {
return (
<React.Fragment>
<FieldArray
name= { props.name }
render={arrayHelper => (
<div>
{
props.values[props.name] && props.values[props.name].length > 0 ?
(
props.values[props.name].map((item, index) => (
<div key={index}>
<Form.Group as= { Row }>
<div className="col-md-8">
<Field name={`${props.name}.${index}`} className="form-control"/>
</div>
<div className="col-md-2">
<Button type="button" variant="outline-secondary"
onClick={() => arrayHelper.remove(index)}>
Remove
</Button>
</div>
<div className="col-md-2">
<Button type="button" variant="outline-secondary"
onClick={() => arrayHelper.insert(index, '')}
>
Add
</Button>
</div>
</Form.Group>
</div>
))
) : (
<Button type="button" variant="outline-secondary" onClick={() => arrayHelper.push('')} >
{`Add ${ props.name }`}
</Button>
)
}
</div>
)}
/>
</React.Fragment>
)
Of course don't forget to add the initial values of the array to the parent component.
And finally when you click on the submit button it would give you the values.

props.mutators is deprecated

My code is working, but I got a warning :
Warning: As of React Final Form v3.3.0, props.mutators is deprecated
and will be removed in the next major version of React Final Form.
Use: props.form.mutators instead. Check your ReactFinalForm render
prop.
with this exemple :
https://codesandbox.io/s/kx8qv67nk5
return <Form
onSubmit={() => console.log('ok')}
mutators={{
...arrayMutators
}}
initialValues={ {customers: [{firstName: 'a', lastName: 'b'}]} }
render={({
handleSubmit,
mutators: { push, pop }, // injected from final-form-arrays above
pristine,
reset,
submitting,
values
}) => {
return (
<form onSubmit={handleSubmit}>
<div className="buttons">
<button
type="button"
onClick={() => push('customers', undefined)}>
Add Customer
</button>
</div>
<FieldArray name="customers">
{({ fields }) =>
fields.map((name, index) => (
<div key={name}>
<label>Cust. #{index + 1}</label>
<Field
name={`${name}.firstName`}
component="input"
placeholder="First Name"
/>
<Field
name={`${name}.lastName`}
component="input"
placeholder="Last Name"
/>
<GithubField name="user1" onSearch={(item) => {
this.api.getByFirstname(item).then(result => console.log(result));
}} />
<span
onClick={() => fields.remove(index)}
style={{ cursor: 'pointer' }}>❌</span>
</div>
))}
</FieldArray>
<div className="buttons">
<button type="submit" disabled={submitting || pristine}>
Submit
</button>
<button
type="button"
onClick={reset}
disabled={submitting || pristine}>
Reset
</button>
</div>
<pre>{JSON.stringify(values, 0, 2)}</pre>
</form>
)
}}
/>
How to do the right way ?
The warning already tells you what to do.
You must use form.mutators instead of just mutators
To do so, you can change your code from
mutators: { push, pop }
to
form: { mutators: { push, pop } }

How to programmatically initialize individual redux-form field?

Struggling with this for two days already. In a 'redux-form' form, I need to prepopulate an order field with a value that comes from array.map iteration index. Here is a complete code for my form (please see comments):
const renderField = ({ input, label, type, meta: { touched, error } }) => {
let color = 'normal';
if (touched && error) {
color = 'danger';
}
return (
<FormGroup color={color}>
<Label>{label}</Label>
<div>
<input {...input} type={type} placeholder={label} />
{touched && (error && <FormFeedback>{error}</FormFeedback>)}
</div>
</FormGroup>
);
};
const renderChoices = ({ fields, meta: { error } }) => (
<ul>
<li>
<button type="button" onClick={() => fields.push()}>
Add Choice
</button>
</li>
{fields.map((choice, index) => (
<li key={index}>
<button type="button" title="Remove Choice" onClick={() => fields.remove(index)}>
x
</button>
<Field name={choice} type="text" component={renderField} label={`Choice #${index + 1}`} />
</li>
))}
{error && <li className="error">{error}</li>}
</ul>
);
const renderQuestion = ({ fields, meta: { error, submitFailed } }) => (
<ul>
<li>
<Button type="button" onClick={() => fields.push({})}>
Add Question
</Button>
{submitFailed && error && <span>{error}</span>}
</li>
{fields.map((question, index) => (
<li key={index}>
<button type="button" title="Remove Question" onClick={() => fields.remove(index)}>
x
</button>
<h4>Question #{index + 1}</h4>
<Field // this is the field that needs to be prepopulated
name={`${question}.order`}
type="text"
component={renderField}
label="Order"
/>
<Field name={`${question}.prompt`} type="text" component={renderField} label="Prompt" />
<FieldArray name={`${question}.choices`} component={renderChoices} />
</li>
))}
</ul>
);
const QuizStepAddForm = props => {
const { handleSubmit, pristine, reset, submitting } = props;
return (
<Form onSubmit={handleSubmit}>
<Field name="order" type="number" component={renderField} label="Quiz Order" />
<Field name="title" type="text" component={renderField} label="Quiz Title" />
<FieldArray name="questions" component={renderQuestion} />
<div>
<Button style={{ margin: '10px' }} color="primary" type="submit" disabled={submitting}>
Submit
</Button>
<Button type="button" disabled={pristine || submitting} onClick={reset}>
Clear Values
</Button>
</div>
</Form>
);
};
export default reduxForm({
form: 'quizStepAddForm',
})(QuizStepAddForm);
I have tried to use redux-form Field API meta props, meta:initial to initialize the field, but by just setting it in the Field tag does not change anything. I also tried to set input:defaultValue in the following manner <Field input={{ defaultValue: '${index + 1}' }}.... This attempt though changes initial value of the wrapped input component yet still, somehow, effects the state of the field, any changes in the field do not have any effect on the form state.
What am I missing?
In order to set the initial state of values inside of a redux form, you need to provide initialValues property into redux form wrapped component:
//... your code above
export default compose(
connect(state => ({
// here, you are having an access to the global state object, so you can
// provide all necessary data as the initial state of your form
initialValues: {
someField: "initial value"
}
})),
reduxForm({
form: "quizStepAddForm"
})
)(QuizStepAddForm);

Resources