JedWatson / react-select

The Select Component for React.js
https://react-select.com/
MIT License
27.56k stars 4.12k forks source link

Input field of mutiselect creatable field is not cleared after new tag cration #1663

Closed Falenos closed 5 years ago

Falenos commented 7 years ago

For reasons unknown the input field of a mutiselect creatable field is not cleared after new tag creation. I click on create newTag, newTag is displayed on the field and next to it the input stays as is

Is there a method I can call when a new tag is stored to clear the input?

I know its supposed to work by default, but in my case it is not happening.

arackaf commented 7 years ago

@Falenos Same here. Here's a gnarly workaround for you - this is MobX code, so yours may be a bit different.

Grab a ref to the creatable component like so

<Creatable 
    disabled={saving || frozen} 
    placeholder="Tag this section" 
    ref={el => this.creatableEl = el}   // <-------- here
    onNewOptionClick={obj => store.addNewTag(obj, this.creatableEl)} 
    onChange={store.setTags} 
    value={store.rawTags} 
    multi={true} 
    options={sectionTagStore.allTags} />

and then

@action addNewTag = (obj, creatableEl) => {
    this.tags.push(sectionTagStore.createTag(obj));
    creatableEl.select.setState({inputValue: ''});  // <------- clears the input text
}

So the ref to the Creatable component has a select property on it which (I assume) is the underlying multi component. Set its state's inputValue to empty string to clear the input text. And done.

Falenos commented 7 years ago

@arackaf thanks a lot for the tip. It got me really close but still not there, it really is a bit gnarly though. I am relatively new in react and from your suggestion I checked out refs for the first time...

What happened is that my component that renders the "Select" is stateless and is not declared as a Class maybe that's why it's not working, but I need to research further. The creatableEl.select.setState(); gives me nothing. There is a State object in Select so creatableEl.select.State exists but the .setState() method is not available. The code is below, if you have any suggestions...

import React, { PropTypes } from "react";
import Select from "react-select";

function TagSelect(props) {
  const handleNewCategoryTagSave = (tag)=> {
    // Blank tags cannot be saved
    if ((!tag || !tag.value) ||
      (typeof tag.value === "string" && tag.value.trim().length === 0)) {
      return false;
    }

    props.onNewCategoryTagSave(props.productId, tag.value, props.name);
  };
  const selectValue = ()=> {
    const value = props.value;
    const multiSelect = props.multi;
    if (!multiSelect && Array.isArray(value)) {
      return props.value[0];
    }

    return value;
  }

  return (
    <Select.Creatable
      multi={props.multi}
      name={props.name}
      options={props.options}
      placeholder={props.placeholder}
      value={selectValue()}
      onChange={(value) => props.onChange(value, props.name)}
      onNewOptionClick={handleNewCategoryTagSave}
    />
  );
}

TagSelect.propTypes = {
  multi: PropTypes.bool,
  name: PropTypes.string,
  onChange: PropTypes.func,
  onNewOptionClick: PropTypes.func,
  options: PropTypes.arrayOf(PropTypes.object),
  placeholder: PropTypes.string,
  value: PropTypes.any
};

export default TagSelect;
arackaf commented 7 years ago

@Falenos not sure. You should be able to add the ref to the Creatable in your SFC, and then whatever event handler that needs to use it would be called from within the closure, with access to the ref.

At this point though I'd just make your component a class; I don't see any real value to making an SFC bloated like that. imo SFCs fit better with simpler components that aren't doing very much.

Falenos commented 7 years ago

@arackaf will do and get back on you, thanks

ghost commented 7 years ago

Just ran into this issue, the suggestion worked perfectly. Thanks for that, @arackaf. Would be neat if the input was cleared automatically by default, but not sure what other kinds of issues that might create.

smelted-code commented 7 years ago

@arackaf I ran into this, too. I'm relatively new to the library; is this low-hanging fruit for contributors, or are there a bunch of edgy cases that would trip me up if I tried? I'm also using react-select in a stateless component, so adding this.some_ref breaks my paradigm a bit.

arackaf commented 7 years ago

@smelted-code there are currently 133 open pull requests. I'd advise you NOT to PR this unless the owner of this project tells you he'll accept it. I mean, if you want to try to fix it just to see if you can, and won't mind if the PR is ignored, then go for it. But don't go trying to fix it unless you're ok with the PR lingering for a bit.

No offense intended to the project's owner. We all have limited time during the week - I just don't want a new contributor to get frustrated :)

Falenos commented 7 years ago

@arackaf I made progress on the issue with a different approach.

Because we needed some functionality that wasn't there either way, like being able to delete the Options form the dropdown Menu we forked it and we will release our enhanced version probably by the end of the month from @artlimes, in case you are interested.

Eitherway from my current knowledge of how thing work this is what I saw.

There is a method in Select.js calledselectValue(). It is called whenever an Option is selected and clears the Select state property inputValue. This method is NOT called when you add a new Option.

If you decide to make your own fork or edit the core somehow like we did, my first approach was to create a new method in Select.js e.g clearInput that simply this.setState({inputValue: ''}). Then you can call it from Creatable.js at

createNewOption () {
            if (isOptionUnique) {
                if (onNewOptionClick) {
                    onNewOptionClick(option);
                    // Our addition. Clears the input values on click.
                    this.select.clearInput(option);
                } else {
                    ...
                }
            }
        }
    },

There are also 2 boolean props that might become handy if you pass them to Select from you app. These are onBlurResetsInputand onCloseResetsInput. The onCloseResetsInput refers to the situations when the dropdown Menu is closing.

So, my current approach is calling the existing Select closeMenu() method fromCreatable.js instead of our custom clearInput and combine it with a onCloseResetsInput={true}. This is fine for our usecase but as you can imagine, it closes the Menu when a new option is created.

My general advice would be to check the methods that setState({inputValue: ''}) and figure out which one you want to use. Hope this helped, cheers

arackaf commented 7 years ago

@Falenos - thanks a ton for the info. This project is great - look forward to seeing what's ahead for it.

faniva commented 7 years ago

The following worked for me :

<Select.Creatable
   name="sticker_categories"
   value={this.state.categories}
   options={this.state.categoriesSuggestions}
   multi={true}
   simpleValue={false}
    joinValues={true}
   // loadOptions={this.getTags}
   onChange={this.onCategoriesSelectChange.bind(this)}
   promptTextCreator={ label => `Create category "${label}"`}
   onNewOptionClick={this.onCreateNewCategory.bind(this)}
   newOptionCreator={ o => { return {label: o.label, value: o.label.toLowerCase()  } } }
    ref={ s => this.selector = s}
 />

Notice the ref attribute is a function that stores the component into this.selector variable Then I have something like this for the onNewOptionClick handler:

onCreateNewCategory(newCatg){
        console.log(this.selector);
        console.log(newCatg);

        const catgs = [].concat(this.state.categories, newCatg);

        this.setState({ categories : catgs})

        this.selector.select.selectValue(); // this.selector points to the component itself and makes its methods available
    }
hsimah commented 7 years ago

@josefano thanks for that, it almost entirely worked for me. I found that in my onChange function the value included an undefined element. Easy to filter that out and work around this annoying issue!

Falenos commented 5 years ago

I am closing this :) i think the discussion is obsolete at this point