props.mutators is deprecated - reactjs

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 } }

Related

Reset input on uncheck checkbox (ReactJS + Formik)

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>

Formik Validation of semantic-ui-react select-field(dropdown)

Hi guys i would like to validate semantic-ui form with a selectInput (dropdown) but it brings a warning and it does not work. Here is the warning:
Formik called handleChange, but you forgot to pass an "id" or "name" attribute to your input:
<div role="option" aria-checked="false" aria-selected="false" class="item" style="pointer-events: all;"><span class="text">value Two</span></div>Formik cannot determine which value to update.
Formik validates the textInput properly but for the selectInput it brings the above warning and nothing is taken in the handleSubmit function.
Below is the snip of the code please.
<Formik
initialValues={{ levelValue: "", attachLevel: "" }}
validationSchema={Yup.object({
levelValue: Yup.string().required("Required Please"),
attachLevel: Yup.string().required("Required Please")
})}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
>
{({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting
}) => (
<Form onSubmit={handleSubmit}>
<Form.Group>
<Form.Input
name="levelValue"
placeholder="Define Level..."
onChange={handleChange}
onBlur={handleBlur}
value={values.levelValue}
type="text"
/>
{touched.levelValue && errors.levelValue ? (
<span className="span_error">
{errors.levelValue}
</span>
) : null}
</Form.Group>
<Form.Group>
<Form.Input
control={Select}
name="attachLevel"
onChange={handleChange}
onBlur={handleBlur}
defaultValue={values.attachLevel}
multiple
options={Class}
/>
{touched.attachLevel && errors.attachLevel ? (
<span className="span_error">
{errors.attachLevel}
</span>
) : null}
</Form.Group>
<Button
color="blue"
type="submit"
className="submit"
disabled={isSubmitting}
>
<Icon name="add" />
Attach
</Button>
</Form>
)}
</Formik>
Any Help will be appreciable please....

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.

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);

redux-form, setting a field visibility based on other field value

I have a complex form, where visibility of some fields is dependent on the value of other fields.
How is setting a field's visibility based on a value of other field done in redux-form?
const renderField = ({ input, label, type, display='flex', meta: { touched, error } }) => (
<div style={ { display: display }}>
<label>{label}</label>
<div>
<input {...input} type={type} placeholder={label}/>
{touched && error && <span>{error}</span>}
</div>
</div>
)
const renderMembers = ({ fields, meta: { touched, error } }) => (
<ul>
<li>
<button type="button" onClick={() => fields.push({})}>Add Member</button>
{touched && error && <span>{error}</span>}
</li>
{fields.map((member, index) =>
<li key={index}>
<button
type="button"
title="Remove Member"
onClick={() => fields.remove(index)}/>
<h4>Member #{index + 1}</h4>
<Field
name={`${member}.firstName`}
type="text"
component={renderField}
label="First Name"/>
<Field
name={`${member}.lastName`}
type="text"
component={renderField}
label="Last Name"/>
<Fields names={[ `${member}.firstName`, `${member}.lastName` ]} component={renderFields}/>
<FieldArray name={`${member}.hobbies`} component={renderHobbies}/>
</li>
)}
</ul>
)
Let's assume that you have a form with 2 fields:
const fields = [
'foo',
'bar'
]
reduxForm(
{
form: 'AwesomeForm',
fields
}
)(AwesomeForm)
To control a field visibility based on other field value in the <AwesomeForm/> component, you can set corresponding display value with inline styles. Just make a verification, that checks if other value satisfies needed conditions. Like:
export const AwesomeForm = (props) => {
return (
<form onSubmit={props.handleSubmit}>
<TextField
{...props.fields.foo}
/>
<TextField
{...props.fields.bar}
style={{
display: props.fields.name.foo === 'some value' ? 'none' : 'block',
}}
/>
<button
type='submit'
/>
</form>
)
}

Resources