React-select: Get the individual option that was added or removed onChange?

Created on 7 Dec 2016  路  6Comments  路  Source: JedWatson/react-select

Maybe I'm missing something, but I can't seem to find it anywhere in the docs. When using multi select, the onChange handler only returns the new array of items. There is no way to tell which option was added or removed.

Is there any handler that can tell me which option was removed and which option was added instead of a complete array of currently selected options?

Most helpful comment

yeah, this library is incredible, but i found this design decision difficult to understand

I'm just using a simple diff function onChange for now.

Here's a snippet of my solution. I just picked this library up today, so it's likely this isn't the optimal way to do this, but I'm procrastinating atm, so what the hell. Note a few caveats for my use case:

  1. since I am using Creatable, I've assigned the handler to onNewOptionClick as well
  2. rather than filter through my options as an array of objects, I've just used the simpleValue option so that my onChange handler receives a comma-delimited string instead of an array of objects. Unnecessary for an example this trivial, but on larger arrays, I imagine this might make a difference.

This works for my use case, but given how customizable this component is, I doubt it's copy-pasta for everyone.

// these two props are passed in from above
const allTags = [
  { value: 'one', label: 'Fake Tag' },
  { value: 'two', label: 'Fake Tag2' },
  { value: 'three', label: 'Fake Tag3' },
  { value: 'four', label: 'Fake Tag4' },
]
const currentTags = ['one', 'two']


const AddTagDealy = (props) => {

  const _onChange = (newTags) => {
    const { currentTags } = props
    // split on whatever your delimiter is
    const newTagsArray = newTags.split(',')

    // is this a tag addition or deletion? find which array is longer to find out
     const isDeletion = newTagsArray.length < currentTags.length

    // note brackets are just es6 array destructuring
    const [ alteredValue ] = isDeletion ?
        // if deletion
      currentTags.filter(tag => !newTagsArray.includes(tag))
      // if addition
      : newTagsArray.filter(tag => !currentTags.includes(tag))

    console.log('CHANGED VALUE', alteredValue, "Was it a deletion?", isDeletion)
  }

  const { allTags, currentTags } = props

  return (
    <Select.Creatable
      name='form-field-name'
      value={currentTags}
      multi
      backspaceRemoves={false}
      deleteRemoves={false}
      clearable={false}
      simpleValue
      onNewOptionClick={_onChange}
      options={allTags}
      onChange={_onChange}
      />
  )
}

So if user clicks to delete 'Fake Tag2', the console logs two, and true

If they add the custom value 'Fake Tag99', the console logs 'Fake Tag99' and false

It's a bit inconsistent, because clicking an item gets you the value, while custom creations get you what amounts to the label, but afaik it's the only way.

All 6 comments

yeah, this library is incredible, but i found this design decision difficult to understand

I'm just using a simple diff function onChange for now.

Here's a snippet of my solution. I just picked this library up today, so it's likely this isn't the optimal way to do this, but I'm procrastinating atm, so what the hell. Note a few caveats for my use case:

  1. since I am using Creatable, I've assigned the handler to onNewOptionClick as well
  2. rather than filter through my options as an array of objects, I've just used the simpleValue option so that my onChange handler receives a comma-delimited string instead of an array of objects. Unnecessary for an example this trivial, but on larger arrays, I imagine this might make a difference.

This works for my use case, but given how customizable this component is, I doubt it's copy-pasta for everyone.

// these two props are passed in from above
const allTags = [
  { value: 'one', label: 'Fake Tag' },
  { value: 'two', label: 'Fake Tag2' },
  { value: 'three', label: 'Fake Tag3' },
  { value: 'four', label: 'Fake Tag4' },
]
const currentTags = ['one', 'two']


const AddTagDealy = (props) => {

  const _onChange = (newTags) => {
    const { currentTags } = props
    // split on whatever your delimiter is
    const newTagsArray = newTags.split(',')

    // is this a tag addition or deletion? find which array is longer to find out
     const isDeletion = newTagsArray.length < currentTags.length

    // note brackets are just es6 array destructuring
    const [ alteredValue ] = isDeletion ?
        // if deletion
      currentTags.filter(tag => !newTagsArray.includes(tag))
      // if addition
      : newTagsArray.filter(tag => !currentTags.includes(tag))

    console.log('CHANGED VALUE', alteredValue, "Was it a deletion?", isDeletion)
  }

  const { allTags, currentTags } = props

  return (
    <Select.Creatable
      name='form-field-name'
      value={currentTags}
      multi
      backspaceRemoves={false}
      deleteRemoves={false}
      clearable={false}
      simpleValue
      onNewOptionClick={_onChange}
      options={allTags}
      onChange={_onChange}
      />
  )
}

So if user clicks to delete 'Fake Tag2', the console logs two, and true

If they add the custom value 'Fake Tag99', the console logs 'Fake Tag99' and false

It's a bit inconsistent, because clicking an item gets you the value, while custom creations get you what amounts to the label, but afaik it's the only way.

Did the same as @brandonmp.
I've a list of item already selected for my item. In the onChange function of the Select i do this:

  • Check if it's a deletion with a if(this.state.myItemSelectedValues.length > onChangeNewVal)
  • Parse my this.state.myItemSelectedValues array
  • If my onChangeNewVal array does not include my this.state.myItemSelectedValues[i] (use .includes() function), I unshift or push it in my available options array

Thanks for the tip. Had this same problem.

Faced the same issues, hope it will help someone

import 'difference' from 'lodash/difference'
this.currentTags = []
handleChange = (options) => {
    const optionsValue = options.map(({ value }) => value)
    if (this.currentTags.length < options.length) {
      const addedElement = difference(optionsValue, this.currentTags)[0]
      this.currentTags.push(addedElement)
      console.log("addedElement",addedElement)
      //call custom add event here
    }
    else {
      let removedElement = difference(this.currentTags, optionsValue)[0]
      console.log("removedElement", removedElement)
      // call custom removed event here
      let index = this.currentTags.indexOf(removedElement)
      if (index !== -1) {
        this.currentTags.splice(index, 1)
      }
    }
  }

The file difference what does he contain??

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

Related issues

yrabinov picture yrabinov  路  3Comments

coder-guy22296 picture coder-guy22296  路  3Comments

batusai513 picture batusai513  路  3Comments

ericj17 picture ericj17  路  3Comments

pashap picture pashap  路  3Comments