Using props inside of a field-array in redux-from - reactjs

I have a redux-form stateless component that contains a field array. I'm trying to determine how to pass a prop to a contained field in order to populate select list options.
const renderSoftwareForm = ({ fields, meta: { error, submitFailed }, props}) => (
<ul style={{listStyle: "none", padding: "0"}}>
<li>
<button type="button" onClick={() => fields.push({})}>
Add Line Item
</button>
</li>
{fields.map((softwares, index) => (
<li key={index}>
<Row>
<Col sm={1}>
<Field
label="Theme"
name={`${softwares}.theme`}
type="select"
component={SelectComponent}
>
<option>test</option>
{props.themes.data && props.themes.data.map(themes => (
<option value={themes.prodLine} key={themes.prodLineId}>
{themes.prodLine}
</option>
))}
</Field>
</Col>
</Row>
<div style={{textAlign: "center"}}>
<button type="button" onClick={() => fields.remove(index)}>
Remove Line Item
</button>
</div>
<div>
<span> </span>
</div>
</li>
))}
</ul>
)
const SoftwareForm = props => {
const { handleSubmit, pristine, reset, submitting } = props
return (
<div>
<FieldArray name="softwares" props={props} component={renderSoftwareForm} />
</div>
)
}
export default SoftwareForm;
Everything works with static values in the options list.
I can see the props being passed all the way to renderSoftwareForm, but it doesn't seem to be available inside of my Field... I feel like I'm missing something easy... props.themes.data returns an error --- Cannot read property 'themes' of undefined ---... How do I access this prop in the FieldArray Field?
Thanks!

<Field
label="Theme"
name={`${softwares}.theme`}
type="select"
component={SelectComponent}
props={{
themes: props.themes
}}
>
<option>test</option>
{props.themes.data && props.themes.data.map(themes => (
<option value={themes.prodLine} key={themes.prodLineId}>
{themes.prodLine}
</option>
))}
</Field>
This way themes props will be avaiilable in you selectComponent

Related

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 add initial Values in field from redux-form if I use map for create array values

How to add initial Values in field from redux-form if I use map for create array values.It is my code.
I'm trying to create an initial value for a field named longHours
{this.state.tem.map((value, index) => (
<div key={index} className="d-flex justify-content-start humanOnProject align-items-center">
<img src={this.state.tem[index].img} className="humanLogo" />
<div className="reSelect">
<Select
options={peoples}
value={value}
name="form-field-re"
clearable={false}
onChange={(selectedOption) => this.reChange(selectedOption, index)}
className="reSelectHuman"
/>
<div className="HumanPosition">{this.state.tem[index].position}</div>
</div>
<Field name={`Tems[${index}].temstart`} type="text" component={TemStartproject} />
<Field
name={`Tems[${index}].longHours`}
type="text"
component="input"
className="longHours"/>
<button type="button" title="Remove Member" onClick={() => this.removeArry(index)} className="deleteBtn" />
</div>
))}
Just map value to initialValues props
connect(
state => ({
initialValues: state.formdata
}),
{ }
)(Component)
more information
https://redux-form.com/7.3.0/examples/initializefromstate/
if your field in array then need to FieldArray name with index
like
{ fieldArrayName : [{temstart: 'data', longHour : 122323 }]}

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

How to supply an initial value to an individual Field in redux-form?

I am trying to build a form using FieldArrays from redux-form. Each field in the fields array needs to have an "order" field. I want to prepopulate this field with a default/initial value. I know how to supply initialValues to the form, but cannot figure out how to do it for an individual Field.
const renderQuestion = ({ fields, meta: { error, submitFailed } }) => (
<ul>
{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
name={`${question}.order`}
value={index + 1} // This line is an example to what I am trying to achieve
type="number"
component={renderField}
label="Order" />
<Field name={`${question}.prompt`} type="text" component={renderField} label="Prompt" />
</li>
))}
</ul>
);
const QuizStepAddForm = props => {
...
<FieldArray name="questions" component={renderQuestion} />
...
};
<Field meta={{initial: `${index + 1}}} />
https://redux-form.com/7.1.1/docs/api/field.md/#-code-meta-initial-any-code-

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