I have a redux-form and react-select working together. react-select has an the ability to create a new select option on the fly by typing into the select text field.
In my example you will see that I have updated the view so that the newly created option is highlighted as selected and appears in the select field.
However, this does not update my form json unless I click the newly created option that looks as though its already selected.
To replicate: type in select box - you will notice that the new created option is highlighted and select field updated. But you will also see that the json values remain unchanged. Click on the newly created option, you will see the values change.
Codesandbox example: https://codesandbox.io/s/k2v2922nq5
import React from "react";
import { Field, reduxForm, FieldArray } from "redux-form";
import TextField from "material-ui/TextField";
import { RadioButton, RadioButtonGroup } from "material-ui/RadioButton";
import Checkbox from "material-ui/Checkbox";
import SelectField from "material-ui/SelectField";
import MenuItem from "material-ui/MenuItem";
import asyncValidate from "./asyncValidate";
import validate from "./validate";
import CreatableSelect from "react-select/lib/Creatable";
const CustomStyle = {
option: (base, state) => ({
...base,
display: "inline",
marginRight: "10px",
backgroundColor: state.isSelected ? "#00285C" : "#eee",
cursor: "pointer"
}),
menuList: () => ({
padding: 10,
display: "inline-flex"
}),
menu: () => ({
position: "relative"
})
};
const createOption = (label: string) => ({
label,
value: label.toLowerCase().replace(/\W/g, "")
});
class LastNameSelectInput extends React.Component {
constructor(props) {
super(props);
}
state = {
value: this.props.options[0],
options: this.props.options
};
handleCreate = (inputValue: any) => {
this.setState({ isLoading: true });
setTimeout(() => {
const { options } = this.state;
const newOption = createOption(inputValue);
this.setState({
isLoading: false,
options: [...options, newOption],
value: newOption
});
}, 1000);
};
render() {
const { input, options } = this.props;
return (
<div>
<style>
{`.react-select__dropdown-indicator,.react-select__indicator-separator {
display: none;
}`}
</style>
<CreatableSelect
classNamePrefix="react-select"
options={this.state.options}
menuIsOpen={true}
onChange={value => {
let newValue = input.onChange(value);
this.setState({ value: newValue });
}}
onBlur={() => input.onBlur(input.value)}
onCreateOption={this.handleCreate}
value={this.state.value}
styles={CustomStyle}
isClearable
/>
</div>
);
}
}
const MaterialUiForm = props => {
const { handleSubmit, options, pristine, reset, submitting } = props;
return (
<form onSubmit={handleSubmit}>
<div>
<Field name="firstName" component={LastNameSelectInput} {...props} />
</div>
</form>
);
};
export default reduxForm({
form: "MaterialUiForm", // a unique identifier for this form
validate,
asyncValidate
})(MaterialUiForm);
So when you create a new option, you're setting the React component state, but not doing anything to let redux-form know, which uses the Redux store as its state. This is always a big source of confusion (React component state vs. Redux store).
The solution is to let redux-form know that we want to select our newly created option as well. This can be done by dispatching the onChange
manually.
I forked your sandbox and added the manual dispatch.
Notable changes (omitting existing code for brevity):
handleCreate
// takes the input to fire dispatch upon
handleCreate = input => (inputValue: any) => {
...
this.setState(...);
input.onChange(newOption); // manually dispatching the change to let redux-form know
render
// pass a ref to the input in handleCreate
onCreateOption={this.handleCreate(input)}
redux-form let's you manually trigger change using field name as well but a bound input
is the most preferable.