react-select

React-select not able to add items by just typing them in and clicking out of field


I am using react-select, in particular the Creatable component. My users are confused when they try to add a new item and they just type it in and then click somewhere else on the page. It doesn't actually create the item. I see that "enter" key or clicking the "add" link will add it but this is not intuitive for my users. Is there a way to force it to add the item when the user clicks away from the element?

I tried seeing if there were any props I could use to control this behavior. https://react-select.com/props#creatable-props


Solution

  • Yes, you can pass a ref to the CreatableSelect component, and you can also attach an onBlur event handler to it.

    Let me explain each part in detail:

    1. Passing a ref to CreatableSelect: The CreatableSelect component from the react-select library allows you to pass a ref if you need direct access to the DOM or component instance. This is useful if you want to trigger methods or access the component programmatically.
    import React, { useState } from "react";
    import CreatableSelect from "react-select/creatable";
    
    const createOption = (label) => ({
      label,
      value: label.toLowerCase().replace(/\W/g, ""),
    });
    
    const defaultOptions = [
      createOption("One"),
      createOption("Two"),
      createOption("Three"),
    ];
    
    const SelectOptionList = () => {
    
      const ref = React.useRef();
    
      const [isLoading, setIsLoading] = useState(false);
      const [options, setOptions] = useState(defaultOptions);
      const [value, setValue] = useState();
    
      const handleCreate = (inputValue) => {
        setIsLoading(true);
        setTimeout(() => {
          const newOption = createOption(inputValue);
          setIsLoading(false);
          setOptions((prev) => [...prev, newOption]);
          setValue(newOption);
        }, 1000);
      };
    
      return (
        <CreatableSelect
          ref={ref}
          isClearable
          isDisabled={isLoading}
          isLoading={isLoading}
          onChange={(newValue) => setValue(newValue)}
          onCreateOption={handleCreate}
          options={options}
          value={value}
        />
      );
    };
    
    1. Handling the onBlur event: The onBlur event in CreatableSelect is triggered when the component loses focus, meaning when the user clicks outside of the select element. You can pass your own onBlur handler to run custom logic when this happens.
    import React, { useState } from "react";
    import CreatableSelect from "react-select/creatable";
    
    const createOption = (label) => ({
      label,
      value: label.toLowerCase().replace(/\W/g, ""),
    });
    
    const defaultOptions = [
      createOption("One"),
      createOption("Two"),
      createOption("Three"),
    ];
    
    const SelectOptionList = () => {
      const [isLoading, setIsLoading] = useState(false);
      const [options, setOptions] = useState(defaultOptions);
      const [value, setValue] = useState();
    
      const handleCreate = (inputValue) => {
        setIsLoading(true);
        setTimeout(() => {
          const newOption = createOption(inputValue);
          setIsLoading(false);
          setOptions((prev) => [...prev, newOption]);
          setValue(newOption);
        }, 1000);
      };
    
      const handleOnBlur = () => {
        console.log('Blur triggered')
      };
    
      return (
        <CreatableSelect
          isClearable
          isDisabled={isLoading}
          isLoading={isLoading}
          onChange={(newValue) => setValue(newValue)}
          onCreateOption={handleCreate}
          options={options}
          value={value}
          onBlur={handleOnBlur}
        />
      );
    };
    
    

    Combining ref and onBlur

    You can combine both ref and onBlur by passing both props to the CreatableSelect component. The ref allows you to manage focus or programmatically interact with the component, while onBlur allows you to handle the loss of focus and execute your own logic.

    Complete code:

    import React, { useState } from "react";
    import CreatableSelect from "react-select/creatable";
    
    const createOption = (label) => ({
      label,
      value: label.toLowerCase().replace(/\W/g, ""),
    });
    
    const defaultOptions = [
      createOption("One"),
      createOption("Two"),
      createOption("Three"),
    ];
    
    const SelectOptionList = () => {
      const ref = React.useRef();
    
      const [isLoading, setIsLoading] = useState(false);
      const [options, setOptions] = useState(defaultOptions);
      const [value, setValue] = useState();
    
      const handleCreate = (inputValue) => {
        setIsLoading(true);
        setTimeout(() => {
          const newOption = createOption(inputValue);
          setIsLoading(false);
          setOptions((prev) => [...prev, newOption]);
          setValue(newOption);
        }, 1000);
      };
    
      const handleOnBlur = () => {
        const inputValue = ref.current.props.inputValue;
    
        if (inputValue) {
          handleCreate(inputValue);
        }
      };
    
      return (
        <CreatableSelect
          ref={ref}
          isClearable
          isDisabled={isLoading}
          isLoading={isLoading}
          onChange={(newValue) => setValue(newValue)}
          onCreateOption={handleCreate}
          options={options}
          value={value}
          onBlur={handleOnBlur}
        />
      );
    };
    

    That's it. Let me know how it went :)