Redux-form Material-UI dropdown not showing dropdown, selection - reactjs

Im using Material-ui select #material-ui/core/Select with redux-form as a HOC. I have created the form and select box appears but the options are not showing.
here is my component
import React from "react";
import { Field, reduxForm } from "redux-form";
import SelectField from "#material-ui/core/Select";
import MenuItem from "#material-ui/core/MenuItem";
const renderSelectField = ({
input,
label,
meta: { touched, error },
children,
...custom
}) => (
<SelectField
floatingLabelText={label}
errorText={touched && error}
{...input}
onChange={(event, index, value) => input.onChange(value)}
children={children}
{...custom}
/>
);
const Form = props => {
const { handleSubmit, pristine, reset, submitting } = props;
return (
<form onSubmit={handleSubmit}>
<div>
<Field
name="favoriteColor"
component={renderSelectField}
label="Favorite Color"
>
<MenuItem value="1" primaryText="value 1" />
<MenuItem value="2" primaryText="value 2" />
<MenuItem value="3" primaryText="value 3" />
</Field>
</div>
<div>
<button type="submit" disabled={pristine || submitting}>
Submit
</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>
Clear Values
</button>
</div>
</form>
);
};
export default reduxForm({
form: "form"
})(Form);
It appears as below
when clicked the combobox options as here
And I have created a codesandbox instance
https://codesandbox.io/s/l2pqoryvvl?fontsize=14

Simply change the MenuItems to
<MenuItem value="1">value 1</MenuItem>
<MenuItem value="2">value 2</MenuItem>
<MenuItem value="3">value 3</MenuItem>
menu item does not take a primary text as a prop.

#S.Haviv has mentioned when i change
<MenuItem value="1" text="value 1" />
to
<MenuItem value='1'>value 1</MenuItem> the menu items appeared but the click isn't success. so I had to get rid of the onclick handler in the Select field
and also I added a label for the component
Here is the code for complete component
https://codesandbox.io/s/l2pqoryvvl?fontsize=14
import React from "react";
import { Field, reduxForm } from "redux-form";
import SelectField from "#material-ui/core/Select";
import MenuItem from "#material-ui/core/MenuItem";
import InputLabel from '#material-ui/core/InputLabel';
import FormControl from '#material-ui/core/FormControl';
const renderSelectField = ({
input,
label,
meta: { touched, error },
children,
...custom
}) => (
<FormControl>
<InputLabel >{label}</InputLabel>
<SelectField
floatingLabelText={label}
errorText={touched && error}
{...input}
children={children}
{...custom}
/>
</FormControl>
);
const Form = props => {
const { handleSubmit, pristine, reset, submitting } = props;
return (
<form onSubmit={handleSubmit}>
<div>
<Field
name="value"
component={renderSelectField}
label="Choose a Value"
value="1"
>
<MenuItem value="1">value 1 </MenuItem>
<MenuItem value="2">value 2 </MenuItem>
<MenuItem value="3">value 3 </MenuItem>
</Field>
</div>
<div>
<button type="submit" disabled={pristine || submitting}>
Submit
</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>
Clear Values
</button>
</div>
</form>
);
};
export default reduxForm({
form: "form" // a unique identifier for this form
})(Form);

Related

Dynamically change value in Formik when a state changes

I need to change the value in my form fields every time a state (called selectedProduct, its input field is not included in Formik tag) changes.
I tried:
putting the value I want in the initialValues (does not work obviously because at the first render I haven't choosen my selectedProduct yet)
putting the value I want in the "value" props in every field in formik.
It almost work: Datepicker gets the right value, select box input does not get any value(idk why), text field take the right value but this is covered by the label. And that's because none of the fields get validated.
This is what I've done, with the two points above applied
import React, { useState } from 'react';
import * as Yup from 'yup';
import {
Formik, Form, ErrorMessage, Field,
} from 'formik';
import {
Button, TextField,
MenuItem,
} from '#material-ui/core';
import DateFnsUtils from '#date-io/date-fns';
import {
MuiPickersUtilsProvider,
KeyboardDatePicker,
} from '#material-ui/pickers';
const validationSchema = Yup.object().shape({
startValidityDate: Yup.date().required(),
discount: Yup.string().required(),
days: Yup.string().required(),
});
const MyComponent = ({filteredProduct, daysList}) => {
const [selectedProduct, setSelectedProduct] = useState('');
const onChangeProduct = (product) => {
setSelectedProduct(product.target.value);
};
const handleRunButton = (newExecution) => {
console.log(newExecution);
};
return (
<div>
<div className={classes.productComboWrapper}>
<div id="selectBoxNotIncludedInFormik">
<TextField
margin="normal"
style={{}}
variant="outlined"
name="productId"
id="productId"
fullWidth
select
label="Select product"
value={selectedProduct?.id}
onChange={(product) => onChangeProduct(product)}
>
<MenuItem key="" value="">
{StringsConst.noneSelected}
</MenuItem>
{filteredProduct?.map((el) => (
<MenuItem key={el} value={el}>
{el.isin}
</MenuItem>
))}
</TextField>
</div>
</div>
<Formik
initialValues={{
startValidityDate: selectedProduct?.startValidityDate,
days: selectedProduct?.coupon?.days,
discount: selectedProduct?.discount,
}}
validationSchema={validationSchema}
onSubmit={(values) => {
const newExecution = {
startValidityDate: values.startValidityDate,
days: values.days,
discount: values.discount,
};
handleRunButton(newExecution);
}}
>
{({
errors, dirty, setFieldValue, values,
}) => (
<Form>
<div className={classes.datePicker}>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<KeyboardDatePicker
disableToolbar
label="Start Validity Date"
inputVariant="outlined"
variant="inline"
autoOk
fullWidth
disabled
format="dd/MM/yyyy"
value={selectedProduct?.startValidityDate}
onChange={(dt) => setFieldValue('startValidityDate', dt)}
KeyboardButtonProps={{
'aria-label': 'change date',
}}
/>
</MuiPickersUtilsProvider>
</div>
<div className={classes.fieldWrapper}>
<Field
className={classes.field}
name="discount"
as={TextField}
variant="outlined"
margin="normal"
fullWidth
id="discount"
autoComplete="discount"
placeholder="Discount"
disabled
value={selectedProduct?.discount}
/>
</div>
<div className={classes.textFieldWrapper}>
<TextField
margin="normal"
style={{}}
variant="outlined"
name="days"
id="days"
fullWidth
select
label="Days"
disabled
value={selectedProduct?.coupon?.days}
onChange={(val) => setFieldValue('days', val.target.value)}
>
<MenuItem key="" value="">
{StringsConst.noneSelected}
</MenuItem>
{daysList.map((el) => (
<MenuItem key={el} value={el}>
{el}
</MenuItem>
))}
</TextField>
</div>
<div className={classes.buttonContainer}>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
{StringsConst.Run}
</Button>
</div>
</Form>
)}
</Formik>
</div>
)
}
So, the three input fields in the form are disabled, but I need them to fill when I choose a value in the first select box outside of the form.
Can you suggest me another approach?
You can connect input or button to the form outside the form.
like this code:
<form id="myForm">
<button> click me </button>
</form>
<input type="text" form="myForm"/>
ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefform

React multiple select form saving only one option to array

I'm trying to create a multiple select form. But when I submit, only the first option is saved into the array, not all selected options. I'm getting options from locations collection, and I'm submitting the form to the categories collection. What can be the problem?
const handleInputChange = (e) => {
setCategory({ ...category, [e.target.name]: e.target.value });
};
return (
<div>
<form onSubmit={handleSubmit}>
<div className='form-group'>
<select
name='location'
onChange={handleInputChange}
size={locations.length}
className='form-control'
multiple
>
{locations.map((location) => (
<option value={location.title}>
{location.title}
</option>
))}
</select>
</div>
<button>Add</button>
</form>
</div>
);
Please check this example:
import React from 'react';
import MenuItem from '#material-ui/core/MenuItem';
import FormControl from '#material-ui/core/FormControl';
import Select from '#material-ui/core/Select';
export default class DropDownMenuExample extends React.Component {
constructor(props) {
super(props);
this.state = {value: []};
}
handleChange(event) {
let value = event.target.value;
this.setState({value})
};
render() {
return (
<div>
<FormControl>
<Select
labelId="demo-controlled-open-select-label"
id="demo-controlled-open-select"
value={this.state.value}
multiple
onChange={this.handleChange.bind(this)}
>
<MenuItem value={1}>Blue</MenuItem>
<MenuItem value={2}>Red</MenuItem>
<MenuItem value={3}>Green</MenuItem>
<MenuItem value={4}>Black</MenuItem>
<MenuItem value={5}>Purple</MenuItem>
<MenuItem value={6}>Yellow</MenuItem>
<MenuItem value={7}>Maroon</MenuItem>
<MenuItem value={8}>Gray</MenuItem>
<MenuItem value={9}>Silver</MenuItem>
<MenuItem value={10}>Gold</MenuItem>
</Select>
</FormControl>
</div>
);
}
}

How to use custom radio component with react-final-form?

I am trying to use a custom Radio component with React-final-form but it is not acting as a radio button but as a checkbox, ie, all the buttons are open for selection.
The 3rd party Radio button has the following schema:
checked boolean
Whether or not radio is checked
onChange () => void
Called when the user attempts to change the checked state
name string
The input name, used to reference the element in JavaScript
I created a custom Component for using the Radio Component:
const CustomRadio = (props: any) => (
<Radio
{...props.input}
{...props.rest}
name={props.name}
onChange={() => props.input.onChange()}
/>
)
and I am using it as follows:
<Field name="food"
component={CustomRadio}
value="1"
/>
<Field name="food"
component={CustomRadio}
value="2"
/>
Being very new to RFF and new to React, I may be doing something very wrong, hence any help will be appreciated.
Basically, I want to use RFF with my 3rd party components. Though I have been successful to use my Input component with RFF as expected, Radio Button is the one creating problems.
Here is the correct implementation for Radio with react-final-form:-
https://codesandbox.io/s/vibrant-easley-5n1ek?file=/index.js
/* eslint-disable jsx-a11y/accessible-emoji */
import React from "react";
import { render } from "react-dom";
import Styles from "./Styles";
import { Form, Field } from "react-final-form";
import RadioGroup from "#material-ui/core/RadioGroup";
import FormControlLabel from "#material-ui/core/FormControlLabel";
import FormControl from "#material-ui/core/FormControl";
import Radio from "#material-ui/core/Radio";
import FormLabel from "#material-ui/core/FormLabel";
const RadioWrapper = (props) => {
const {
input: { checked, value, name, onChange, ...restInput },
} = props;
return (
<Radio
name={name}
inputProps={restInput}
onChange={onChange}
checked={checked}
value={value}
/>
);
};
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const onSubmit = async (values) => {
await sleep(300);
window.alert(JSON.stringify(values, 0, 2));
};
const App = () => {
return (
<Styles>
<h1>React Final Form - Simple Example</h1>
<a
href="https://final-form.org/react"
target="_blank"
rel="noopener noreferrer"
>
Read Docs
</a>
<Form
onSubmit={onSubmit}
initialValues={{
employed: false,
all_sub_tenants: "true"
}}
render={({ handleSubmit, form, submitting, pristine, values }) => (
<form onSubmit={handleSubmit}>
<FormControl component="fieldset">
<FormLabel component="legend">
Select Tenants
</FormLabel>
<RadioGroup aria-label="allSubTenants" name="allSubTenants">
<FormControlLabel
value="true"
control={
<Field
name="all_sub_tenants"
component={RadioWrapper}
type="radio"
value={"true"}
/>
}
label="All Sub-Tenants"
/>
<FormControlLabel
value="false"
control={
<Field
name="all_sub_tenants"
component={RadioWrapper}
type="radio"
value={"false"}
/>
}
label="Select Sub-Tenants"
/>
</RadioGroup>
</FormControl>
<div>
<label>Notes</label>
<Field name="notes" component="textarea" placeholder="Notes" />
</div>
<div className="buttons">
<button type="submit" disabled={submitting || pristine}>
Submit
</button>
<button
type="button"
onClick={form.reset}
disabled={submitting || pristine}
>
Reset
</button>
</div>
<pre>{JSON.stringify(values, 0, 2)}</pre>
</form>
)}
/>
</Styles>
);
};
render(<App />, document.getElementById("root"));

Control the Redux-form with the button

I am using the Redux-form to do a task.
This form in a form container.
In the form container or in the form component.
There are two buttons. An add button and a subtract button.
The form component is:
import React from 'react'
import { Field, reduxForm } from 'redux-form'
import TextField from 'material-ui/TextField'
import RaisedButton from 'material-ui/RaisedButton'
const renderTextField = ({ input, label, meta: { touched, error }, ...custom }) => (
<TextField hintText={label}
floatingLabelText={label}
errorText={touched && error}
{...input}
{...custom}
/>
)
const ActivityDetailForm = props => {
const { handleSubmit, pristine, reset, submitting,} = props
return (
<form onSubmit={handleSubmit}>
<div>
<RaisedButton
type="submit"
disabled={pristine || submitting}
label="saveChange"
fullWidth={true}
secondary={true}
/>
</div>
</form>
)
}
export default reduxForm({
form: 'ActivityDetailForm', // a unique identifier for this form
})(ActivityDetailForm)
Now, I face a problem. When I click the add button,
<div>
<Field name="field1" component={renderTextField} label="text1: "/>
</div>
the code above will be created in the form element.
When I click the add button again, the div element which includes the Field named field2 will be created in the form element.
... Field named field3
... Field named field4
... Field named field5
...
When I click the subtract button. The last Field element will be destroyed.
Do you know the method to solve this problem?
The following (untested) is a pretty basic example on how to achieve dynamic inputs with a FieldArray. You'd have to tweak this a bit to tailor it to your specific scenario.
const renderTextField = ({ input, label, meta: { touched, error }, ...custom }) => (
<TextField hintText={label}
floatingLabelText={label}
errorText={touched && error}
{...input}
{...custom}
/>
)
const ActivityDetailForm = props => {
const { handleSubmit, pristine, reset, submitting,} = props
const renderFieldArray = ({ fields }) => (
<div>
<div>
<RaisedButton
onTouchTap={() => fields.push({})}
label="Add"
/>
</div>
{fields.map((field, index) => (
<div>
<div key={index}>
<Field
name={`${field}.name`}
label={`Text ${index + 1}`}
component={renderTextField}
/>
</div>
<div>
<RaisedButton
onTouchTap={() => fields.remove(index)}
label="Remove"
/>
</div>
</div>
))}
</div>
);
return (
<form onSubmit={handleSubmit}>
<div>
<FieldArray
name="textFields"
component={renderFieldArray}
/>
<RaisedButton
type="submit"
disabled={pristine || submitting}
label="saveChange"
fullWidth={true}
secondary={true}
/>
</div>
</form>
)
}
export default reduxForm({
form: 'ActivityDetailForm', // a unique identifier for this form
})(ActivityDetailForm)

ReduxForm: using formValueSelector within a FieldArray for conditional fields doesn't render immediately

This sort of works, except additional block doesn't show up when I make a service_type radio selection, it only pops up/re-renders if I complete an additional action, like adding or removing a service block or changing another field.
import './Register.scss';
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm, FieldArray, formValueSelector } from 'redux-form';
import MenuItem from 'material-ui/MenuItem';
import { RadioButton } from 'material-ui/RadioButton'
import {
TextField,
SelectField,
TimePicker,
RadioButtonGroup
} from 'redux-form-material-ui';
import validate from './validate';
class OnboardPageThree extends Component {
static propTypes = {
handleSubmit: PropTypes.func.isRequired,
previousPage: PropTypes.func.isRequired
}
constructor(props, context) {
super(props, context);
}
renderGroups = ({ fields, meta: { touched, error } }) => {
return (
<div>
{fields.map((service, index) =>
<div className="service-type-group" key={index}>
<h4>{index} Service</h4>
<button
type="button"
className="remove-button"
onClick={() => fields.remove(index)}>Remove
</button>
<div className="field-group half">
<Field
name={`${service}.service_type`}
component={RadioButtonGroup}
floatingLabelText="Service type"
className="textfield">
<RadioButton value="first_come_first_serve" label="First-come first-served"/>
<RadioButton value="appointments" label="Appointment"/>
</Field>
</div>
{/* Sort of works except doesn't populate until forced re-render by adding another to array or updating a previous field */}
{typeof this.props.services[index].service_type != undefined && this.props.services[index].service_type == "first_come_first_serve" ? <div className="fieldset">
<p className="label">Timeslot capacity and length when creating a first-come-first-served (line-based) service blocks</p>
<div className="field-group sentence-inline">
<Field
name={`${service}.max_people`}
type="number"
component={TextField}
label="Number of people per timeslot"
className="textfield"
/>
<p>people are allowed every</p>
<Field
name={`${service}.time_interval`}
component={SelectField}
className="textfield">
<MenuItem value="5" primaryText="5 minutes" />
<MenuItem value="10" primaryText="10 minutes" />
<MenuItem value="15" primaryText="15 minutes" />
<MenuItem value="30" primaryText="30 minutes" />
<MenuItem value="60" primaryText="60 minutes" />
<MenuItem value="all_day" primaryText="All day" />
</Field>
<p>.</p>
</div>
</div> : null}
</div>
)}
<button
type="button"
className="action-button"
onClick={() => fields.push({})}>Add Service
</button>
{touched && error && <span className="error-message">{error}</span>}
</div>
);
}
render() {
const { handleSubmit, previousPage } = this.props;
return (
<form onSubmit={handleSubmit}>
<h2>When do you provide service?</h2>
<FieldArray name="service_options" component={this.renderGroups} />
<div className="justify-flex-wrapper service-actions">
<button type="button" className="back-button" onClick={previousPage}>Back</button>
<button type="submit" className="action-button">Next</button>
</div>
</form>
);
}
}
OnboardPageThree = reduxForm({
form: 'onboarding',
destroyOnUnmount: false,
validate
})(OnboardPageThree)
const selector = formValueSelector('onboarding');
OnboardPageThree = connect(
(state) => {
const services = selector(state, 'service_options');
return {
services
};
}
)(OnboardPageThree);
export default OnboardPageThree;
Yes, that is true. The FieldArray does not re-render on the change of any of its items (that would be really bad for large arrays), only on size changes.
You will have to create a separate connected component for your array items. I have worked up a working demo here.

Resources