React-select: Value do not get selected in async component

Created on 19 Dec 2017  路  11Comments  路  Source: JedWatson/react-select

Please have a look at this code, When I click on the drop down it shows 10 values, If I select any value from these values it get selected.

But when i search in drop down for example if I type test. It then shows 10 values matching the given string ( test ). But if i try to select any value from the new data set ( which matched the 'test' input ) value do not get selected.

getOptions = (input: string, callback: Function) => {
        this.props.action(
            input,
            /* callback */
            (res: Object): void => {
                if ( res && res.data ) {
                    callback(null, { options: res.data });
                } else {
                    callback([]);
                }
            }
        );
    };

render() {
        const { name, label, value, multi, placeholder, onChange } = this.props;

        return (<Async
            name={name}
            label={label}
            value={value}
            placeholder={placeholder}
            autoload
            loadOptions={this.getOptions}
            onChange={onChange}
            async
            multi={multi}
        />);
    }

Most helpful comment

@saadbinsaeed Doc just dont get updated yet. i will do this tommorow. u need to pass {value: '', label: ''} instead of string

All 11 comments

@saadbinsaeed Doc just dont get updated yet. i will do this tommorow. u need to pass {value: '', label: ''} instead of string

@gen1321 where should it be passed? In value?

yep, value should be { value: '...', label: '...' } object. Is there any possibility to get current options?

please update docs or update this issue

Check the last comment here. Solved the same issue for me

what about multiple values?

I just spend the last couple of hours trying to implement this async multiselect with redux form. I needed to get just the value from the selection sent to redux form (or comma separated string of values rather), but I couldn't use that to display the select option on screen because Async only displays the selected options when the value you feed it is an array of objects like... [{value: '', label: ''}]. The simpleValue prop doesn't seem to play nicely with the Async component either which is how I'm working with the Select component. It will output simpleValue onChange, but you can't use that as a value to display the selections. Anyhow this is my component...

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Async } from 'react-select';
import _ from 'lodash';
import { client } from '../../../actions/client';

class ReduxFormMultiSelect extends Component {
  constructor(props) {
    super(props);
    this.state = {
      values: []
    };

    this.handleInputChange = this.handleInputChange.bind(this);
    this.getOptions = this.getOptions.bind(this);
  }

  getOptions(input) {
    if (!input) {
      return Promise.resolve({ options: [] });
    }

    return client.get(`${this.props.optionsEndpoint}?q=${input}&select=true`)
      .then(response => (
        { options: response.data }
      ));
  }

  handleInputChange(values) {
    // send selection options array to state
    this.setState({ ...this.state, values });
    // pull selected values into array
    const vals = _.map(values, 'value');
    // join array into comma separated string for redux form
    this.props.onChange(vals.join(','));
  }

  render() {
    const { input, className } = this.props;

    return (
      <Async
        {...input}
        placeholder={'Select one or more...'}
        className={className}
        multi
        // simpleValue
        disabled={false}
        onBlurResetsInput={false}
        loadOptions={this.getOptions}
        onChange={this.handleInputChange}
        value={this.state.values}
      />
    );
  }
}

export default ReduxFormMultiSelect;

ReduxFormMultiSelect.propTypes = {
  optionsEndpoint: PropTypes.string,
  input: PropTypes.object,
  onChange: PropTypes.func,
  onBlur: PropTypes.func
};

I am passing { value: '...', label: '...' } but it does not seem to display the selected value in the box. here is my component. when i select an item my state variable holds {value: "3", label: "ACG_242-0_non_working_flows_to_test_-Template-v6.3.xls"} which gets passed onto the Async component but it does not display the value even though it is in the list

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import FontAwesome from 'react-fontawesome';
import { isServiceName, isServiceRef } from '../../scripts/validation';
import {
    fetchExemptionType,
    fetchMarkets,
    fetchServiceDomains,
    fetchServicesList,
    fetchExemptionsForCommsmatrix,
    fetchSecurityEngineerList,
    fetchBusinessPriorityList
} from '../../actions/security';
import { fetchCommsmatricesByService } from '../../actions/commsmatrices';
import axios from 'axios';
import { Field, reduxForm } from 'redux-form';
import PropTypes from 'prop-types';
import { Select, Async } from "react-select";
import 'react-select/dist/react-select.css';
import { Checkbox } from 'react-bootstrap';
import {
    BootstrapTable,
    TableHeaderColumn
} from 'react-bootstrap-table';
import 'react-bootstrap-table/dist/react-bootstrap-table-all.min.css';

const ASYNC_DELAY = 500;

class SecurityExemptionsNew extends Component {
    constructor(props) {
        super(props);
        this.handleSaveBtnClick = this.handleSaveBtnClick.bind(this);
        this.handleShowError = this.handleShowError.bind(this);
        this.initData = this.initData.bind(this);
        this.renderSelectField = this.renderSelectField.bind(this);
        this.renderTextField = this.renderTextField.bind(this);
        this.updateCommsMatrix = this.updateCommsMatrix.bind(this);
        this.renderSelect2Field = this.renderSelect2Field.bind(this);
        this.clearValue = this.clearValue.bind(this);
        this.updateValue = this.updateValue.bind(this);
        this.getCommsmatrices = this.getCommsmatrices.bind(this);
        this.fnToggleCheckboxes = this.fnToggleCheckboxes.bind(this);
        this.handleCheckedValues = this.handleCheckedValues.bind(this);
        this.meta = {
            title: 'Request Exemption',
            description: 'This section allows you to request an exemption'
        };
        this.passMetaBack = this.passMetaBack.bind(this);
        this.runOnce = false;
        this.state = {
            loaded: false,
            isLoading: true,
            //country: 'AU',
            //disabled: false,
            searchable: true,
            selectValue: { value: '0', label: 'All'},
            clearable: true,
            rtl: false,
            options: [
                { value: '0', label: 'All'}
                ],
            toggleChecked: true,

        };
        this.exemptionType = {};
        this.markets = {};
        this.serviceDomain = {};
        this.servicesList = {};
        this.matrices = {};
        this.securityEngineerList = {};
        this.businessPriority = {}
        this.tableOptions = {
                paginationShowsTotal: false,
                sizePerPageList: [ {
                    text: '10', value: 10
                  }, {
                    text: '50', value: 50
                  }, {
                      text: '100', value: 100
                    } 
                  ],
            };
        this.checkedValues = [];
    }

    passMetaBack = () => {
        this.props.passMetaBack(this.meta);
    };

    handleShowError(errorMsg) {
        this.props.handleShowError({ show: true, errorMsg: errorMsg });
    }

    handleSaveBtnClick = () => {
        const { columns, onSave } = this.props;
        let params = new URLSearchParams();
        columns.forEach((column, i) => {
            if (column.field == 'source') {
                params.append(column.field, 'CCP');
            } else if (column.field == 'owner') {
                params.append(column.field, this.props.username);
            } else if (column.field !== undefined) {
                params.append(column.field, this.refs[column.field].value);
            }
        }, this);
        // You should call onSave function and give the new row
        //onSave(newRow);
        //api call

        if (!isServiceName(params.get('name'))) {
            //alert
            console.log('Invalid name');
            return;
        }

        if (!isServiceRef(params.get('ref'))) {
            //alert
            console.log('Invalid ref');
            return;
        }

        params.append('quiet', 1);
        let self = this;
        this.props.addServices(params).then(function(response) {
            let data = response.payload.data;
            if (data.header.error) {
                //@todo show error on input form
                self.handleShowError(data.header.message);
            } else {
                let newRow = {};
                newRow = data.body.recordset.record;
                onSave(newRow);
                self.sendDataBack(newRow);
            }
        });
    };

    initData() {
        let self = this;
        let promises = [];
        promises.push(this.props.fetchExemptionType());
        promises.push(this.props.fetchMarkets());
        promises.push(this.props.fetchServiceDomains());
        promises.push(this.props.fetchServicesList());
        promises.push(this.props.fetchSecurityEngineerList());
        promises.push(this.props.fetchBusinessPriorityList());
        axios.all(promises).then(function(response) {

            let data = response[0].payload.data;
            self.exemptionType = data.body.recordset.record;
            data = response[1].payload.data.body.recordset.record;
            self.markets = Object.keys(data).map(key => 
                 data[key]
            );
            data = response[2].payload.data;
            self.serviceDomain = data.body.recordset.record;
            data = response[3].payload.data;
            self.servicesList = data.body.recordset.record;
            data = response[4].payload.data;
            self.securityEngineerList = data.body.recordset.record;
            data = response[5].payload.data;
            self.businessPriority = data.body.recordset.record;
            self.setState({ loaded: true });
        });
    }

    updateCommsMatrix(service_id){

        if(service_id > 0){
            let self = this;
            this.props.fetchCommsmatricesByService(service_id)
            .then( response => {
                let data = response.payload.data.body.recordset.record;
                let options = self.populateOptionsObject(data, 'id', 'filename');               
                options.unshift({ value: '0', label: 'All'});
                return options;
            })
            .then( options => this.setState({options:options,isLoading: false}));
        }else{
            this.setState({options:{ value: '0', label: 'All'},isLoading: false});
        }
    }

    populateOptionsObject(options, key, value) {
        return options.map((option, index) => (
                {value : option[key], label: option[value] }
        ));
    }

    populateOptions(options, key, value) {
        return options.map((option, index) => (
            <option key={option.id} value={option[key]}>
                {option[value]}
            </option>
        ));
    }

    renderSelectField(field) {

        let options = [];
        if (field.label == 'Type') {
            options = this.populateOptions(this.exemptionType, 'name', 'name');
        } else if (field.label == 'Market') {
            options = this.populateOptions(this.markets, 'name', 'name');
        } else if (field.label == 'Service Domain') {
            options = this.populateOptions(this.serviceDomain, 'name', 'name');
        } else if (field.label == 'Service/Project/Programme') {
            options = this.populateOptions(this.servicesList, 'id', 'name');
            options.unshift(
                <option key="0" value="0" selected>
                    All
                </option>
            );
        } else if (field.label == 'Comms Matrix') {
            options.unshift(
                <option key="0" value="0" selected>
                    All
                </option>
            );
        } else if (field.label == 'Security Engineer') {
            options = this.populateOptions(this.securityEngineerList, 'email', 'email');
            options.unshift(
                <option key="0" value="" selected>
                    --Select Engineer--
                </option>
            );
        } else if (field.label == 'Business Priority') {
            options = this.populateOptions(this.businessPriority, 'name', 'name');
        }
        let id = "select_" + field.input.name;
        return (
            <div className="form-group">
                <label className="control-label col-sm-2">{field.label}</label>
                <div className="col-sm-10">
                    <select
                        id={id}
                        {...field.select}
                        name={field.input.name}
                        className="form-control form-control-inline"
                        type="select"
                        onChange={event => {
                              //props.input.onChange(event); // <-- Propagate the event
                              this.updateCommsMatrix(event.target.value);
                            }}
                    >
                        {options}
                    </select>
                </div>
            </div>
        );
    }

    clearValue (e) {
        this.select.setInputValue('');
    }

    updateValue (newValue) {
        if(newValue !== null){
            this.props.fetchExemptionsForCommsmatrix(newValue.value)
            .then( response => {
                if(this.state.toggleChecked){
                    this.checkedValues = response.payload.data.body.recordset.record.map((option, index) => (
                            {id : option['id'] }
                    ));
                }
                this.setState({selectValue: newValue, policies : response.payload.data.body.recordset.record});
            });
        }else{
            this.setState({selectValue: {value:"0",label:"All"}});
        }

    }

    getCommsmatrices (input, callback) {
        input = input.toLowerCase();
        let options = this.state.options.filter(i => {
            return i.label.toLowerCase().includes(input) == true;
            //return i.label.toLowerCase().substr(0, input.length) === input;
        });

        let data = {
            options: options,//.slice(0, MAX_CONTRIBUTORS),
            complete: true //options.length <= MAX_CONTRIBUTORS,
        };

        setTimeout(function() {
            callback(null, data);
        }, ASYNC_DELAY);
    }

    gotoCommsmatrix (value, event) {
        if(value.value > 0){
            window.open(window.top.protocol + '://' + window.top.hostname + '/api/user/commsmatrix/id/'+value.value+'/format/xml');
        }
    }
    /*
    <Select
        id={id}
        //className="col-sm-6"
        ref={(ref) => { this.select = ref; }}
        onBlurResetsInput={false}
        onSelectResetsInput={false}
        autoFocus
        options={options}
        simpleValue
        clearable={this.state.clearable}
        name={field.input.name}
        //disabled={this.state.disabled}
        value={this.state.selectValue}
        onChange={this.updateValue}
        rtl={this.state.rtl}
        searchable={this.state.searchable}
    />
     */
    renderSelect2Field(field){
        let id = "select_" + field.input.name;
        return(
                <div className="form-group">
                    <label className="control-label col-sm-2">{field.label}</label>
                    <div className="col-sm-4">

                        <Async 
                            name={field.input.name}
                            multi={false} 
                            value={this.state.selectValue} 
                            onChange={this.updateValue} 
                            valueKey="value" 
                            labelKey="label" 
                            loadOptions={this.getCommsmatrices} 
                            onValueClick={this.gotoCommsmatrix}
                            cache={false}
                            isLoading={this.state.isLoading}
                            optionRenderer={(option) => {return option.label;}}
                        />
                    </div>
                </div>
        );
    }

    renderTextField(field) {
        let value = '';
        if (field.label == 'Requestor') {
            value = this.props.activeUser.email;
        }
        return (
            <div className="form-group">
                <label className="control-label col-sm-2">{field.label}</label>
                <div className="col-sm-10">
                    <input
                        name={field.input.name}
                        className="form-control form-control-inline"
                        type="text"
                        {...field.text}
                        value={value}
                    />
                </div>
            </div>
        );
    }

    renderTextareaField(field) {
        return (
            <div className="form-group">
                <label className="control-label col-sm-2">{field.label}</label>
                <div className="col-sm-10">
                    <textarea
                        name={field.input.name}
                        className="form-control form-control-inline"
                        type="textarea"
                        {...field.text}
                    />
                </div>
            </div>
        );
    }

    handleCheckedValues({target}){
        if (target.checked){        
            target.setAttribute('checked', true);
            this.checkedValues.push({id:$(target).val()});
        }else{
            target.removeAttribute('checked');
            let arr = this.checkedValues.filter(function(item) { 
                return item.id !== $(target).val()
            })
            this.checkedValues = arr;
        }
    }

    actionButtons(cell, row, enumObject, rowIndex) {
        let checked = true;
        let arr = this.checkedValues.filter(function(item) { 
            return item.id == row.id
        });
        if(arr.length == 0){
            checked = false;
        }
        return (
            <input type="checkbox" defaultChecked={checked} name="comms_matrix_flow[]" value={row.id} onClick={this.handleCheckedValues} />
        );          
    }

    fnToggleCheckboxes({target})  {
        this.setState({toggleChecked : !this.state.toggleChecked})
        let elements = document.getElementsByName("comms_matrix_flow[]");
        let length = elements.length;
        if(!this.state.toggleChecked){
            this.checkedValues = this.state.policies.map((option, index) => (
                    {id : option['id'] }
            ));
            for(let i = 0;i<length;i++){
                elements[i].checked = true;
            }
        }else{
            this.checkedValues = [];

            for(let i = 0;i<length;i++){
                elements[i].checked = false;
            }
        }
    }

    renderPolicies(){
        if(this.state.selectValue !== null && parseInt(this.state.selectValue.value) > 0){
            return (
                <div className="form-group">
                    <label className="control-label col-sm-2">Select Denied Flow(s): 
                        <br/> <input name="toggle_comms_matrix_flow" type="checkbox" onClick={this.fnToggleCheckboxes} defaultChecked={true} />
                    Toggle All 
                        <br/> <font style={{color: "#ff0000", fontSize: "11px"}}>
                    Only select and group flows relevant to the exemption request to
                    prevent it being declined. </font></label>
                    <div className="col-sm-10">
                        <BootstrapTable
                            keyField="id"
                            data={this.state.policies}
                            options={this.tableOptions}
                            pagination
                            striped
                            hover
                            tableHeaderClass="table-vf thead"
                         >
                            <TableHeaderColumn dataFormat={this.actionButtons.bind(this)} width={`30px`}></TableHeaderColumn>
                            <TableHeaderColumn dataField="src">Source</TableHeaderColumn>
                            <TableHeaderColumn dataField="dst">Destination</TableHeaderColumn>
                            <TableHeaderColumn dataField="protocol" >Protocol</TableHeaderColumn>
                            <TableHeaderColumn dataField="ports">Ports</TableHeaderColumn>
                            <TableHeaderColumn dataField="type" width={`60px`}>Type</TableHeaderColumn>
                        </BootstrapTable>
                     </div>
                 </div>

            );
        }
        return (<div className="form-group"></div>);
    }

    render() {
        console.log(this);
        if (!this.runOnce && this.props.isReady) {
            this.runOnce = true;
            this.initData();
        }
        const { handleSubmit } = this.props;
        let form = 'Loading...';
        if (this.state.loaded) {
            let policiesTable = this.renderPolicies();
            return (
                <div className="container-fluid">
                    <form onSubmit={handleSubmit} className="form-horizontal">
                        <Field
                            label="Type"
                            loadFrom="exemptionType"
                            name="exemption_type"
                            component={this.renderSelectField}
                        />
                        <Field
                            label="Market"
                            loadFrom="markets"
                            name="market"
                            component={this.renderSelectField}
                        />
                        <Field
                            label="Service Domain"
                            loadFrom="serviceDomain"
                            name="service_domain"
                            component={this.renderSelectField}
                            type="select"
                        />
                        <Field
                            label="Service/Project/Programme"
                            loadFrom="servicesList"
                            name="service_id"
                            component={this.renderSelectField}
                            type="select"
                        />

                        <Field
                            label="Demand Ref"
                            name="demand_ref"
                            component={this.renderTextField}
                            type="text"
                        />
                        <Field
                            label="Exemption Summary"
                            name="name"
                            component={this.renderTextField}
                            type="text"
                        />
                        <Field
                            label="Exemption Description"
                            name="description"
                            component={this.renderTextareaField}
                            type="textarea"
                        />
                        <Field
                            label="Comms Matrix"
                            loadFrom="matrices"
                            name="comms_matrix_id"
                            component={this.renderSelect2Field}
                            type="select"
                        />
                        {policiesTable}
                        <Field
                            label="Requestor"
                            name="requestor"
                            component={this.renderTextField}
                            type="text"
                            readonly="readonly"
                        />
                        <Field
                            label="Security Engineer"
                            loadFrom="securityEngineerList"
                            name="security_engineer"
                            component={this.renderSelectField}
                            type="select"
                        />
                        <Field
                            label="Link to Design Doc"
                            name="designdoc"
                            component={this.renderTextField}
                            type="text"
                        />
                        <Field
                            label="Business Priority"
                            loadFrom="businessPriority"
                            name="business_priority"
                            component={this.renderSelectField}
                            type="select"
                        />
                        <Field
                            label="Expiry date (dd-MMM-yy)"
                            name="expiry_date"
                            component={this.renderTextField}
                            type="text"
                        />
                        <div className="form-group">
                            <div className="col-sm-offset-2 col-sm-10">
                                <button id="btnSubmit" type="button" name="btnSubmit" className="btn btn-vodafone hidden-print">Submit</button>
                            </div>
                        </div>
                    </form>
                </div>
            );
        }

        return <div className="container-fluid">{form}</div>;
    }
}

function mapStateToProps(state) {
    return {
        securityExemptions: state.securityExemptions,
        commsmatrices: state.commsmatrices
    };
}

//Anything returned from this function will end up as props
//on the User container
function mapDispatchToProps(dispatch) {
    // Whenever getUser is called, the result should be passed
    // to all our reducers
    //return {
    //actions: bindActionCreators(Object.assign({}, fetchServices, checkServiceEditable ), dispatch)
    //};
    return bindActionCreators(
        {
            fetchExemptionType,
            fetchMarkets,
            fetchServiceDomains,
            fetchServicesList,
            fetchCommsmatricesByService,
            fetchExemptionsForCommsmatrix,
            fetchSecurityEngineerList,
            fetchBusinessPriorityList
        },
        dispatch
    );
}

SecurityExemptionsNew = connect(mapStateToProps, mapDispatchToProps)(
    SecurityExemptionsNew
);

//Decorate the form component
export default reduxForm({
    form: 'SecurityExemptionsNewForm', // a unique name for this form
    initialValues: { service_domain: 'Corporate (Internal/External)' }
})(SecurityExemptionsNew);

It seems to me when i select a value the input being passed into loadOptions is blank so nothing gets display when selecting a value

in order to onChange work properly you must include two props and your onChange(selected, actionMeta)

  • onSelectResetsInput={false}
  • onBlurResetsInput={false}
    onChange={this._onChange}

ref: https://github.com/JedWatson/react-select/issues/2405#issuecomment-370526500

Hello -

In an effort to sustain the react-select project going forward, we're closing old issues.

We understand this might be inconvenient but in the best interest of supporting the broader community we have to direct our efforts towards the current major version.

If you aren't using the latest version of react-select please consider upgrading to see if it resolves any issues you're having.

However, if you feel this issue is still relevant and you'd like us to review it - please leave a comment and we'll do our best to get back to you!

Was this page helpful?
0 / 5 - 0 ratings