Skip to content

Instantly share code, notes, and snippets.

@leocristofani
Last active June 8, 2020 14:59
Show Gist options
  • Star 31 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save leocristofani/98312e61807db8f32e720c9f97a186e5 to your computer and use it in GitHub Desktop.
Save leocristofani/98312e61807db8f32e720c9f97a186e5 to your computer and use it in GitHub Desktop.
How to integrate React Select with Redux Form
import React, { PropTypes } from 'react';
import Select from 'react-select';
import 'react-select/dist/react-select.css';
RFReactSelect.defaultProps = {
multi: false,
className: ""
};
RFReactSelect.propTypes = {
input: PropTypes.shape({
name: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
onBlur: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
onFocus: PropTypes.func.isRequired,
}).isRequired,
options: PropTypes.array.isRequired,
multi: PropTypes.bool,
className: PropTypes.string
};
export default function RFReactSelect({ input , options, multi, className }) {
const { name, value, onBlur, onChange, onFocus } = input;
const transformedValue = transformValue(value, options, multi);
return (
<Select
valueKey="value"
name={name}
value={transformedValue}
multi={multi}
options={options}
onChange={multi
? multiChangeHandler(onChange)
: singleChangeHandler(onChange)
}
onBlur={() => onBlur(value)}
onFocus={onFocus}
className={className}
/>
);
}
/**
* onChange from Redux Form Field has to be called explicity.
*/
function singleChangeHandler(func) {
return function handleSingleChange(value) {
func(value ? value.value : '');
};
}
/**
* onBlur from Redux Form Field has to be called explicity.
*/
function multiChangeHandler(func) {
return function handleMultiHandler(values) {
func(values.map(value => value.value));
};
}
/**
* For single select, Redux Form keeps the value as a string, while React Select
* wants the value in the form { value: "grape", label: "Grape" }
*
* * For multi select, Redux Form keeps the value as array of strings, while React Select
* wants the array of values in the form [{ value: "grape", label: "Grape" }]
*/
function transformValue(value, options, multi) {
if (multi && typeof value === 'string') return [];
const filteredOptions = options.filter(option => {
return multi
? value.indexOf(option.value) !== -1
: option.value === value;
});
return multi ? filteredOptions : filteredOptions[0];
}
@antholord
Copy link

I'm trying to use your component but I get uncaught ReferenceError: Field is not defined when rendering. Any idea where it's from?

@raj-optisol
Copy link

@antholord its (Field keyword) from Redux-Form https://github.com/erikras/redux-form

@terencechow
Copy link

Is there a way to get this to work with a creatable? I copied the signature that you've used, but the first 'created' tag doens't show up in my form although i see the value passed into redux-form. (Basically replace Select with Creatable in your example and use multi) to see it not work

@bionicvapourboy
Copy link

bionicvapourboy commented Jul 18, 2017

@terencechow same problem, partially solved:
<Field multi name="phones" options={phones} component={RFReactSelect} />
and

export default function RFReactSelect({ input, options, multi, className, meta: { touched, error, warning }, toValidate, ...others }) {
  const { value, onBlur, onChange } = input;

  let transformedValue = List.isList(value) ? value.toJS()
                                            : value;                    
  return (
      <Creatable
        {...input}
        multi={true}
        value={transformedValue}
        onBlur={() =>{onBlur(value)}}
      />
      
  );

The only issue left is about how react-select saves data in redux form state:

[{label:'555-3333444', key:'555-333444'}, {label:...., value:....}....]

instead of

[555-333444, ....]

Forcing onBlur to pass a plain array to the redux state of the form make the component unable to render from state.

@0xpatrickdev
Copy link

0xpatrickdev commented Aug 8, 2017

@bionicvapourboy trying putting simpleValue: true in your Select component to get [555-333444, ....] in your state instead of [{label:'555-3333444', key:'555-333444'}, {label:...., value:....}....]

@0xpatrickdev
Copy link

0xpatrickdev commented Aug 20, 2017

Hey @leocristofani, thank you for this, it is great.

For multi-select inputs, I am getting an error at value: PropTypes.string.isRequired (L13). The component is expecting a string, but Redux Form and React Select both require arrays (note on L66-67).

Perhaps the following would be better:

RFReactSelect.propTypes = {
  input: PropTypes.shape({
    ...
    value:  PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.array,
    ]).isRequired,
   ...
  }).isRequired,
  ...
};

@Misiur
Copy link

Misiur commented Aug 22, 2017

https://github.com/erikras/redux-form/blob/ca00a778af5e2ca5079fc9cac153bd33dd1e313d/src/ConnectedField.js#L106-L110 I have some problems because of this piece of code.
image
Because onChange doesn't receive event object but just a plain value, my custom onChange receives this weird construct. I can bypass it by setting a new onCustomChange callback which is called by your handleSingleValue, but if anybody has a better idea, I'd appreciate it.

@ramoncaldeira
Copy link

I was able to run Creatable successfully by decorating the options parameter before the render function of my custom input component Field.

let values = input.value.split(',');
values.map(value => {
  if(Boolean(value) && !_find(options, {value})) {
   options.push({label: value, value});
  }
});

@addicted2sounds
Copy link

thanks :) you saved my day 👍

@dinshaw
Copy link

dinshaw commented Aug 16, 2018

Thank you for sharing the amazing work. After reading every post out there on react-select/redux-form, this is the only one that works for me. I'm trying to get a default value set, but I'm unsure where to put it. The pattern seems to be to have 'value' come from the component's state, and update that state onChange. I've been playing around with changing export default function RFReactSelect to a full React.Component so I can give it state, but haven't been able to get it right. Any suggestions?
Thank you!

@mciszczon
Copy link

I'm using react 15.4.1, redux-form 7.0.1 and react-select 2.0.0. I've used your code and still got the same problem: values were cleaned from store after the blur event.

Found out, that the onBlur={() => onBlur(value)} override was not needed. After I had removed this line, it started working well! Thanks for the handy snippet!

@gurmeetsinghmalhotra
Copy link

gurmeetsinghmalhotra commented Jun 8, 2020

With the introduction of "actionMeta". How can we incorporate it in "multiChangeHandler" to get the meta information like "name" of the field in our custom onchangehandler?

Currently this is how we have to make it run

handleChangeCreatable = (newValue, actionMeta, prevValue, name) => {
        this.setState({
            [name]: newValue
        });
    };

@leocristofani
Copy link
Author

@gurmeetsinghmalhotra thanks for your comment, but I no longer maintain this gist since I moved away from using Redux and Redux Form long ago. Best of luck!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment