ReactJS: How to wrap react-select in redux-form field?

情到浓时终转凉″ 提交于 2019-12-21 03:36:09

问题


I am working on react-select library and facing some issues, I am using redux-form library and importing <Field /> component from it. So that I can submit the values via form to service.

Below mentioned code works fine, when I use default <Select> from react-select. I can able to select the values from the drop down and the value will be selected even on focus out the value will remain. But selected value is not submitting via form due to redux-form that's why I am wrapping <Select /> component and using with <Field name="sample" component={RenderSelectInput} id="sampleEX" options={options} />

import React from 'react';
import Select from 'react-select';
import RenderSelectInput from './RenderSelectInput'; // my customize select box

var options = [{ value: 'one', label: 'One' }, { value: 'two', label: 'Two' }];


class SelectEx extends React.Component {
    constructor() {
        super();
        this.state = { selectValue: 'sample' }
        this.updateValue = this.updateValue.bind(this);
    }

    updateValue(newValue) {
        this.setState({ selectValue: newValue })
    }

    render() {

        return (
            <div>
                <Select name="select1" id="selectBox" value={this.state.selectValue} options={options} onChange={this.updateValue}/>

                //This works but value won't submit ...

                <Field name="sample" component={RenderSelectInput} id="sampleEX" options={options} />
            //For this, selected value vanishes once I come out of component. 
            </div>
        )
    }
}

export default SelectEx;

But when I use with my customized select (I am wrapping the to submit the value from form) the <Select> component can be visible in UI even the values also. But unable to select the value from dropdown ..., If I select also it displays in the <Select> box but on focus out it vanishes. Please help me ...

RenderSelectInput component:

import React from 'react';
import {Field, reduxForm} from 'redux-form';
import Select from 'react-select';
import 'react-select/dist/react-select.css';


const RenderSelectInput = ({input, options, name, id}) => (

    <div>
        <Select {...input} name={name} options={options} id={id} />
    </div>
)

export default RenderSelectInput;

回答1:


When using react-select with redux-form, you'll need to change the default behavior of onChange and onBlur method and call redux-form's onChange and onBlur method respectively.

So, Try this:

const RenderSelectInput = ({input, options, name, id}) => (
    <Select 
         {...input}
         id={id} 
         name={name} 
         options={options}
         value={input.value}
         onChange={(value) => input.onChange(value)}
         onBlur={(value) => input.onBlur(value)}
    />
)

and use the above component like

<Field component={RenderSelectInput} />

Calling redux-form's onBlur method when focus is removed from the Select field will prevent loss of value.




回答2:


Here this worked for me,

import React, { Component } from 'react';
import Select from 'react-select';
import 'react-select/dist/react-select.css';

export default class RenderSelectInput extends Component {
 onChange(event) {
  if (this.props.input.onChange && event != null) {
   this.props.input.onChange(event.value);
  } else {
   this.props.input.onChange(null);
  }
 }

 render() {
  const { input, options, name, id, ...custom } = this.props;
  return (
   <Select
    {...input}
    {...custom}
    id={id}
    name={name}
    options={options}
    value={this.props.input.value || ''}
    onBlur={() => this.props.input.onBlur(this.props.input.value)}
    onChange={this.onChange.bind(this)}
   />
  );
 }
}

this was extracted from here: https://ashiknesin.com/blog/use-react-select-within-redux-form/




回答3:


Use this which works perfectly and it also handles redux form validation.

import React, {Component} from 'react';
import Select from 'react-select';
import {FormGroup} from "reactstrap";


class CustomSelect extends Component {

 render() {
   const {meta: {touched, error}} = this.props;
   const className = ` form-group mb-3 ${touched && error ? 'has-danger' : '' }`;
   return (    
        <FormGroup>
                <Select
                      {...this.props}
                       value={this.props.input.value}
                       onChange={(value) => this.props.input.onChange(value)}
                       onBlur={() => this.props.input.onBlur(this.props.input.value)}
                       options={this.props.options}
                       placeholder={this.props.placeholder}
                    />
                    <div className={className}>
                         <div className="text-help">
                              {touched ? error : ''}
                          </div>
                     </div>
           </FormGroup>

            );

Use the CustomSelect component in redux form field component as

   <Field
        name='country_name'
        options={this.state.countries}
        component={CustomSelect}
        placeholder="Select your country"
   />



回答4:


Try setting onBlurResetInput property to false.

Something like.

const SelectInput = ({input: { onChange, value }, options, name, id}) => (
    <Select              
      name={name}
      value={value} 
      options={options}
      onChange={onChange}
      onBlurResetsInput={false}
    />
)

Hope this helps!




回答5:


I had to call the onBlur without any argument. The issue with Hardik's answer was, it was not working in iPad (May be also in other iOS or touch devices. I was unable to check).

The onBlur event is automatically triggered along with the onChange event in iPad. It caused the select value to reset to its initial value. So I had to call onBlur method like this,

onBlur={(value) => input.onBlur()}

const RenderSelectInput = ({input, options, name, id}) => (
    <Select 
         {...input}
         id={id} 
         name={name} 
         options={options}
         value={input.value}
         onChange={(value) => input.onChange(value)}
         onBlur={(value) => input.onBlur()}
    />
)

and used as,

<Field component={RenderSelectInput} />



回答6:


The actual problem of wrapping react-select component with redux-form is that Select do need up to date selected option data not only to handle onChange but also to build the options menu. For Select to work properly, selected option data should be of the form:

{
    label: 'Green',
    value: 'green',
}

As redux-form will control Select props you need to store it this way in the store.

So as mentioned you need to overwrite onChange & onBlur handlers & the base way to do it will be:

<Select 
  {...input} 
  onChange={value => input.onChange(value)} 
  onBlur={() => input.onBlur(input.value)} 
  options={options}
/>

If you do not want explicit data to be submitted, your submit form handler is a proper place to process Select value:

const handleSubmit = values => {
  const data = {
      ...values,
      color: values.color,
  };

  // The rest of your submission logic goes here...
}

Note please, that another issue mentioned by Jithin B is onBlur breaking on mobile, which happens due to the fact that react-select calls onChange & onBlur simultaneously on mobile. To solve it in a proper way, call setTimeout inside onBlur handler:

const handleBlur = () => {
   setTimeout(() => {
       const { input } = props;
       input.onBlur(input.value);
   }, 1);
};

You may also want to check Using React Select with Redux Form short but detailed article by Dylan Nielsen, which gives more solid understanding of issues with wrapping Select with redux-form.



来源:https://stackoverflow.com/questions/42650273/reactjs-how-to-wrap-react-select-in-redux-form-field

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!