JedWatson / react-select

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

Focus state not updated after selected a value, when using Components #5584

Open remitoffoli opened 1 year ago

remitoffoli commented 1 year ago

Hi. I've found that when you use components (not all), if you select a value, then click anywhere to "blur" the select, it won't remove the focus state.

I've managed to reproduce it: https://codesandbox.io/s/musing-mcnulty-o4rz92?file=/src/App.tsx

Animation

In my 4 examples :

  1. no components
  2. IndicatorSeparator
  3. SelectContainer (❌ doesn't work)
  4. Control (❌ doesn't work)

The minimal failing code would be:

<Select
  value={currentLetter}
  options={letters}
  onChange={(option) => setCurrentLetter(option || undefined)}
  components={{
    SelectContainer: (p) => <components.SelectContainer {...p} />
  }}
/>

In my real project, I'm using a custom Control to add elements before the input + classNames. And now I can't get the focus state to go off.

Does anyone have an idea about that?

Thanks.

wahmad251 commented 1 year ago

I'm coming across the same exact issue and am still trying to look for a good workaround.

davidquinto commented 1 year ago

A possible workaround can be adding the blurInputOnSelect prop.

tsids commented 1 year ago

According to the documentation,

When defining replacement components, it is important to do so outside the scope of rendering the Select. Defining a replacement component directly in the components prop can cause issues.

The documentation also provides some examples regarding this.

Another thing to note is that the custom component should be defined outside the function that you are exporting, so that the custom component does not re-render every time.

Here is an updated example with working code.

Code:

import { useState } from "react";
import Select, {
  components,
  ContainerProps,
  IndicatorSeparatorProps,
  ControlProps
} from "react-select";

type Opt = { label: string; value: string };

const letters: Opt[] = [
  { label: "A", value: "a" },
  { label: "B", value: "b" },
  { label: "C", value: "c" }
];

// Custom components are defined OUTSIDE App function
const IndicatorSeparator = (props: IndicatorSeparatorProps<Opt, false>) => {
  return (
    <components.IndicatorSeparator {...props}></components.IndicatorSeparator>
  );
};

const SelectContainer = ({ children, ...props }: ContainerProps<Opt>) => {
  return (
    <components.SelectContainer {...props}>
      {children}
    </components.SelectContainer>
  );
};

const Control = (props: ControlProps<Opt, false>) => (
  <div>
    <components.Control {...props} />
  </div>
);

export default function App() {
  const [currentLetter, setCurrentLetter] = useState<Opt>();

  return (
    <>
      <h3>OK</h3>
      <Select
        value={currentLetter}
        options={letters}
        onChange={(option) => setCurrentLetter(option || undefined)}
      />
      <h3>Ok</h3>
      <Select
        value={currentLetter}
        options={letters}
        onChange={(option) => setCurrentLetter(option || undefined)}
        components={{ IndicatorSeparator }}
      />
      <h3>✅ Works Now</h3>
      <Select
        value={currentLetter}
        options={letters}
        onChange={(option) => setCurrentLetter(option || undefined)}
        components={{ SelectContainer }}
      />
      <h3>✅ Works Now</h3>
      <Select
        value={currentLetter}
        options={letters}
        onChange={(option) => setCurrentLetter(option || undefined)}
        components={{ Control }}
      />
    </>
  );
}