Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to set current value or how to enable edit of the selected tag? #1558

Closed
AlexanderTserkovniy opened this issue Feb 16, 2017 · 33 comments
Closed
Labels
issue/reviewed Issue has recently been reviewed (mid-2020)

Comments

@AlexanderTserkovniy
Copy link

Hi guys,

  1. The question is simple how to set current value of the input? Like when I put focus there and see already written text.

  2. Or how to enable editing of the existing tag, that I could click and continue typing?

Thanks.

@jayryemure
Copy link

jayryemure commented Feb 17, 2017

This is critical for my application. I looked into 2 and couldn't find any options to do this and would greatly appreciate a workaround (couldn't come up with that either).

@Siyfion
Copy link

Siyfion commented Feb 27, 2017

Is what you both are referring to is the ability to modify an existing selection's text, ie. Say the selected text is "AAAAA" and you want to select "AAAAB" you want to delete the last letter and change it... You can't. You have to delete the whole thing and start again?

If so, then I wholeheartedly agree, this seems like a really silly little bug... @JedWatson any ideas?

@AlexanderTserkovniy
Copy link
Author

@Siyfion yes, correct it is one option and another one is to set current value, thus we could remove element onclick and add set current value with focus, which will end with editing mode.

@mukulb90
Copy link

mukulb90 commented Mar 7, 2017

@JedWatson I also need this for my application. Any ideas how can we implement it ?

@janusch
Copy link

janusch commented Aug 29, 2017

This would be great to implement. Does maybe one of the 137 PRs of this writing solve this?
Is it correctly understood that editing a selected value should only be possible if it is a Select.Creatable?

@Subha
Copy link

Subha commented Sep 28, 2017

I have this requirement too when the select is Creatable
Ellipses are displayed on the field for long text but not able to place cursor pointer at the end. Not able to edit.

@fzan
Copy link

fzan commented Nov 16, 2017

@JedWatson pushing up this, just noticed that we need the same thing it's opened from February

@jnachtigall
Copy link
Contributor

Looks like this ticket is a dupe of #1868 and the suggested solution is to create your own value and option component which handles the rendering and focus/edit etc. behaviour. I haven't tried myself so would be nice if somebody has a codepen/jsfiddle.

@julian69
Copy link

I've just run into the same issue and haven't been able to find a solution yet.
Does anyone know whether there is a workaround?
I'd really appreciate any help since I'd need to get this feature working asap.
Thanks

@magdalko
Copy link

I see not only me has the same problem! SOLUTION, please come :P

@AlexanderTserkovniy
Copy link
Author

I was not able to resolve it. Had to do some workaround, not even close to the react-select.

@jmaslin
Copy link

jmaslin commented Aug 23, 2018

Has anyone found a solution for this? The library is great but this feature would greatly improve UX.

@jwr
Copy link

jwr commented Aug 31, 2018

Just encountered this problem: I was hoping to replace an editable-text control that I had with react-select, so that text can still be edited, just with a list of suggestions. But I can't, because react-select does not allow editing...

@kimbel
Copy link

kimbel commented Feb 20, 2019

I was not able to resolve it as well.. anyone came up with a solution??

@burtek
Copy link

burtek commented Feb 20, 2019

Two years later... Another +1. @JedWatson ?

@chetan-zalake
Copy link

Another +1. @JedWatson ?
This needs to be added as it is a basic requirement.

@Rall3n
Copy link
Collaborator

Rall3n commented Feb 25, 2019

With v2.x you can overwrite the internal components of the Select with the Components Framework to either replace or enhance functionality.

Here is a very basic example of a CreateableSelect with editable custom option: CodeSandbox.

Easy to implement with the help of the documentation and a look into the core code here on github.

If you are looking for editable option in a simple Select component, you should think about using unique identifiers for your options to identify them if you change the label (and maybe values).

@jonnydodad
Copy link

Another +1, a little shocked this isn't a core feature.
@Rall3n thanks for the info but I'm a little confused by the CodeSandbox.. I want to be able to select an item from the dropdown and then edit it. For example, if I want to select option 'AAAAA', delete the last 'A' and type a 'B' so that the input contains 'AAAAB', I don't have to delete the entire item and start over. I can't seem to make that happen with this example...

@Rall3n
Copy link
Collaborator

Rall3n commented Mar 8, 2019

@jonnydodad Easy (using my example as reference):

Edit onChange to not save the value into either value or createdOption, but into just value:

onChange = (option, action) => {
    // If you want to keep the option in the menu, make a copy of the option as this is a reference to the element in the options array.
    var newOption = Object.assign({}, option);

    this.setState({ value: newOption });
}

Remove the __isNew__ check from SingleValue and always render the input:

SingleValue = ({ children, ...props}) => {
    return (<components.SingleValue {...props}>
      <input
        type="text"
        { ... }
      />
    </components.SingleValue>)
};

Update onEditChange to set state to value instead of createdOption:

onEditChange = (e) => {
    /* ... */
    this.setState({ value: newOption })
}

And for good measure remove the selection of this.state.createdOption from the value prop of CreatableSelect:

<CreatableSelect 
    { ... }
    value={this.state.value}
/>

And et voilà: You can now edit every option you have selected.

@jonnydodad
Copy link

@Rall3n wow awesome thank you so much!

@inottawa
Copy link

inottawa commented Mar 11, 2019

@jonnydodad I went with a different approach.

I pulled a reference to the internals via ref

<CreatableSelect
    ref={ref => {
        this.select = ref;
    }}
/>

I assigned the input value of the ref manually when the component is focused

handleFocus = element => {
    if (this.state.value) {
      this.select.state.inputValue = this.state.value.label;
    }
};

and then removed focus when the menu becomes closed

handleMenuClose = () => {
    this.select.blur();
};

@silverwind
Copy link

silverwind commented Mar 15, 2019

Would be interested in a solution for when isMulti is enabled. Hitting backspace with the cursors after a selected value should make that value editable instead of outright deleting it.

@rusticdeity
Copy link

@inottawa The approach you suggested is working fine if onFocus is triggered on mouse click. If it is happening because of pressing tab key then the behaviour is similar to what we get by default.

Here is the Codesandbox

@SimpleCookie
Copy link

I still need this but for the AsyncSelect. Setting select.state.value etc doesn't work .. and it seems like the AsyncSelect has a select which has a select which has a select .. really weird structure 😄

@punkrats
Copy link

punkrats commented Jan 31, 2020

I was struggling to get AsyncCreatableSelect to be editable. I've downgraded to react-select 2.4.4 because 3.x did not seem to work.

In the example code below, import is not as described in the docs, because I could not fetch react-select/async-creatable. And import AsyncCreatableSelect from 'react-select' seems to yield a regular Select component.

Hope this helps.

'use strict'

import React from 'react'
import { AsyncCreatable } from 'react-select'

export default class MyComponent extends React.Component {
  constructor(props) {
    super(props)

    this.select = null
    this.state = {value: null, isLoading: false, menuIsOpen: false}
    if (props.value) this.state.value = {label: props.value}

    this.setRef = this.setRef.bind(this)
    this.loadOptions = this.loadOptions.bind(this)
    this.onChange = this.onChange.bind(this)
    this.onInputChange = this.onInputChange.bind(this)
    this.onFocus = this.onFocus.bind(this)
    this.onKeyDown = this.onKeyDown.bind(this)
    this.onMenuClose = this.onMenuClose.bind(this)
  }

  // Set reference to the select element.
  // Note the nested `select` objects!
  setRef(element) {
    this.select = element
      ? element.select.select.select
      : null
  }

  // Load options and set loading state.
  loadOptions(value) {
    this.setState({isLoading: true})
    return new Promise(resolve => {
      setTimeout(() => {
        resolve([
          {value: "123", label: "Fake data"}
        ])
        this.setState({isLoading: false})
      }, 1000)
    })
  }

  // Gets called when an option is chosen or created.
  onChange(value) {
    this.setState({value: value})
  }

  // Toggle menu by presence of input.
  // Reset value if input is emptied.
  onInputChange(value, context) {
    this.setState({menuIsOpen: !!value})
    if (!value && context.action === "input-change") {
      this.onChange(null)
    }
  }

  // Restore input value.
  onFocus() {
   if (this.state.value) {
     this.select.handleInputChange({
       currentTarget: {value: this.state.value.label}
     })
   }
  }

  // Close menu when pressing enter. Submit does not work on mobile.
  onKeyDown(event) {
    if (event.keyCode === 13 && !this.state.isLoading) {
      this.onMenuClose()
    }
  }

  // Blur select element to trigger onFocus on next click.
  onMenuClose() {
    this.select.blur()
  }

  render() {
    const { value, menuIsOpen } = this.state

    return (
      <AsyncCreatable
        ref={this.setRef}
        value={value}
        menuIsOpen={menuIsOpen}
        loadOptions={this.loadOptions}

        // Let the select field appear as regular input.
        components={{DropdownIndicator: null, IndicatorSeparator: null}}
        placeholder="Enter your text..."

        // Hook into events to make the editing work.
        onChange={this.onChange}
        onInputChange={this.onInputChange}
        onFocus={this.onFocus}
        onKeyDown={this.onKeyDown}
        onMenuClose={this.onMenuClose}

        // Create the new option at the top of the list.
        createOptionPosition="first"
      />
    )
  }
}

@tuval-rotem-rf
Copy link

Would be interested in a solution for when isMulti is enabled. Hitting backspace with the cursors after a selected value should make that value editable instead of outright deleting it.

here's my example for isMulti new example using @Rall3n as a base
https://codesandbox.io/s/react-select-editable-multi-createable-wp13r

@bladey bladey added the issue/reviewed Issue has recently been reviewed (mid-2020) label Jun 17, 2020
@cention-sany
Copy link

My approach by replacing hidden SingleValue and enchanced Input.

import React, { useCallback, useMemo, useState } from "react";
import { components as cs } from "react-select";
import CreatableSelect from "react-select/creatable";
import { colourOptions } from "./docs/data";

const NewSingleValue = () => null;

const NewInput = ({ value: inputValue, isHidden, ...props }) => {
  const {
    selectProps: { value, getOptionLabel }
  } = props;
  const label = useMemo(() => {
    if (!value) {
      return "";
    }
    return getOptionLabel(value);
  }, [getOptionLabel, value]);
  const v = useMemo(() => {
    if (!inputValue) {
      return label;
    }
    return inputValue;
  }, [inputValue, label]);
  const hidden = useMemo(() => {
    if (v) {
      return false;
    }
    return isHidden;
  }, [isHidden, v]);
  return <cs.Input isHidden={hidden} value={v} {...props} />;
};

const components = {
  ...cs,
  Input: NewInput,
  SingleValue: NewSingleValue
};

const CreatableSingle = props => {
  const [value, setValue] = useState(null);
  const handleInputChange = useCallback((inputValue, actionMeta) => {
    console.group("Input Changed");
    console.log(inputValue);
    console.log(`action: ${actionMeta.action}`);
    console.groupEnd();
  }, []);
  const handleChange = useCallback((value, actionMeta) => {
    console.group("Value Changed");
    console.log(value);
    console.log(`action: ${actionMeta.action}`);
    console.groupEnd();
    setValue(value);
  }, []);
  return (
    <CreatableSelect
      components={components}
      isClearable
      onChange={handleChange}
      onInputChange={handleInputChange}
      options={colourOptions}
      value={value}
    />
  );
};

export default CreatableSingle;

https://codesandbox.io/s/unr3u

Should work for AsyncCreatable too.

@bladey bladey added issue/reviewed Issue has recently been reviewed (mid-2020) and removed issue/reviewed Issue has recently been reviewed (mid-2020) labels Aug 24, 2020
@ebonow
Copy link
Collaborator

ebonow commented Dec 4, 2020

Here is my take at an editable Select.

It's not AsyncCreatable, but should work the same way.

import React, { useState, useRef } from "react";
import Select, { components } from "react-select";

const Input = (props) => <components.Input {...props} isHidden={false} />;

export default function EditableSelect() {
  // This needs to become a controlled component so track state
  const [value, setValue] = useState();
  const [inputValue, setInputValue] = useState("");

  const options = useRef([
    { label: "Editable Options", value: 1 },
    { label: "Black Magic", value: 2 },
    { label: "Very Possible ", value: 3 }
  ]).current;

  const selectRef = useRef();

  const onInputChange = (inputValue, { action }) => {
    // onBlur => setInputValue to last selected value
    if (action === "input-blur") {
      setInputValue(value ? value.label : "");
    }

    // onInputChange => update inputValue
    else if (action === "input-change") {
      setInputValue(inputValue);
    }
  };

  const onChange = (option) => {
    setValue(option);
    setInputValue(option ? option.label : "");
  };

  const onFocus = () => value && selectRef.current.select.inputRef.select();

  return (
    <div style={{ fontFamily: "sans-serif", textAlign: "center" }}>
      <Select
        ref={selectRef}
        options={options}
        isClearable={true}
        value={value}
        inputValue={inputValue}
        onInputChange={onInputChange}
        onChange={onChange}
        onFocus={onFocus}
        controlShouldRenderValue={false}
        components={{
          Input
        }}
      />

      <button onClick={onFocus} disabled={!value} style={{ margin: "1rem 0" }}>
        Hocus Focus!
      </button>
    </div>
  );
}

@ebonow
Copy link
Collaborator

ebonow commented Dec 4, 2020

@cention-sany just a heads up that there is a prop called controlShouldRenderValue that essentially does the same thing by not rendering the single or multi value component.

@ebonow
Copy link
Collaborator

ebonow commented Dec 4, 2020

That all said, this thread is very similar to many others and I have created a discussion here to better discuss the behavior and perhaps what a prop implementation might look like.

I will close this issue and further discussion about this behavior can be had in the provided link.

@Gian-Marco-27
Copy link

Here is my take at an editable Select.

It's not AsyncCreatable, but should work the same way.

import React, { useState, useRef } from "react";
import Select, { components } from "react-select";

const Input = (props) => <components.Input {...props} isHidden={false} />;

export default function EditableSelect() {
  // This needs to become a controlled component so track state
  const [value, setValue] = useState();
  const [inputValue, setInputValue] = useState("");

  const options = useRef([
    { label: "Editable Options", value: 1 },
    { label: "Black Magic", value: 2 },
    { label: "Very Possible ", value: 3 }
  ]).current;

  const selectRef = useRef();

  const onInputChange = (inputValue, { action }) => {
    // onBlur => setInputValue to last selected value
    if (action === "input-blur") {
      setInputValue(value ? value.label : "");
    }

    // onInputChange => update inputValue
    else if (action === "input-change") {
      setInputValue(inputValue);
    }
  };

  const onChange = (option) => {
    setValue(option);
    setInputValue(option ? option.label : "");
  };

  const onFocus = () => value && selectRef.current.select.inputRef.select();

  return (
    <div style={{ fontFamily: "sans-serif", textAlign: "center" }}>
      <Select
        ref={selectRef}
        options={options}
        isClearable={true}
        value={value}
        inputValue={inputValue}
        onInputChange={onInputChange}
        onChange={onChange}
        onFocus={onFocus}
        controlShouldRenderValue={false}
        components={{
          Input
        }}
      />

      <button onClick={onFocus} disabled={!value} style={{ margin: "1rem 0" }}>
        Hocus Focus!
      </button>
    </div>
  );
}
// onInputChange => update inputValue
if (action === 'input-change') {
  setInputValue(inputValue);
  if (inputValue.trim().length == 0 && value) {
    selectRef.current.commonProps.clearValue();
  }
}

@dkrefta
Copy link

dkrefta commented Nov 8, 2023

@Gian-Marco-27 cool man, that was exactly what I was searching for, thanks!

@adnan-smlatic
Copy link

+1 for this

Still happening to me in almost 2025, this feels like a core functionality that should be supported. I have tried everything in this thread and nothing has worked for my scenario. Having to hack together the entire component just to do something like this really doesn't leave a good feeling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
issue/reviewed Issue has recently been reviewed (mid-2020)
Projects
None yet
Development

No branches or pull requests