material-ui icon indicating copy to clipboard operation
material-ui copied to clipboard

[Joy][Select] onChange callback fires without user interaction

Open sibljon opened this issue 2 years ago • 4 comments

Duplicates

  • [X] I have searched the existing issues

Latest version

  • [X] I have tested the latest version

Steps to reproduce 🕹

Link to live example:

https://codesandbox.io/s/select-onchange-callback-mb1n7d?file=/demo.tsx

Steps:

  1. Use a Select component
  2. Have its initial value be anything (for example: null) by specifying the value in the value prop
  3. Change its initial value via the value prop
  4. !! Observe that the onChange handler is called

Current behavior 😯

The onChange callback fires even though the value did not change due to user interaction

Expected behavior 🤔

The onChange callback only fires when the value changes due to user interaction

Context 🔦

It's a common pattern to render a Select and not have its value until later, such as when a network request completes. The owner of the Select component is both specifying the value and the onChange handler, and thus, it doesn't seem necessary that the owner would need to know when the very value that it's supplying has changed.

I've used other UI libraries such as Semantic-UI and UIKit on iOS, and change handlers for UI components are always only called when a value changes due to user interaction. I was hoping that someone here could help me understand why the onChange callback is fired in this case. I first thought this was a bug, but given that there are seemingly no Github Issues related to it, I'm wondering if it's intentional. I noticed that the event is null when the change is not due to user-interaction, so that can at least be used in the short-term to ignore these onChange events when they are not due to user interaction.

Your environment 🌎

npx @mui/envinfo

  System:
    OS: macOS 13.2.1
  Binaries:
    Node: 14.19.1 - ~/.nvm/versions/node/v14.19.1/bin/node
    Yarn: 3.4.1 - /opt/homebrew/bin/yarn
    npm: 6.14.16 - ~/.nvm/versions/node/v14.19.1/bin/npm
  Browsers:
    Chrome: 112.0.5615.49
    Edge: Not Found
    Firefox: 111.0.1 (I'm using Firefox currently)
    Safari: 16.3

sibljon avatar Apr 05 '23 13:04 sibljon

I agree with @sibljon so mark this as a bug. I think this might related to Base UI than Joy UI. @michaldudak Would the latest refactor of the Listbox fixes this?

siriwatknp avatar Apr 14 '23 05:04 siriwatknp

Yes, this issue should be fixed on master (it will be released on Monday or Tuesday).

michaldudak avatar Apr 14 '23 12:04 michaldudak

@michaldudak I'm currently using the latest version (5.0.0-alpha.127) of @mui/base, but I've still come across a similar issue.

onChange callback appears to fire a null value without any user interaction, if the initial value doesn't match any of the available option values. I'm wondering if this behavior is intentional?

Code snippet:

export default function UnstyledSelectSimple() {
  const [value, setValue] = React.useState<string>("");

  return (
    <CustomSelect<string, false>
      value={value}
      onChange={(_, value) => {
        console.log(value); // null
        setValue(value);
      }}
    >
      <StyledOption value="10">Ten</StyledOption>
      <StyledOption value="20">Twenty</StyledOption>
      <StyledOption value="30">Thirty</StyledOption>
    </CustomSelect>
  );
}

Here is a codesandbox example: https://codesandbox.io/s/nifty-kare-miz8r6?file=/demo.tsx

Screenshot 2023-04-26 at 17 34 49

homerchen19 avatar Apr 26 '23 09:04 homerchen19

Right, the issue was still present when you set the initial value to an empty one. I just submitted a PR to fix this.

However, the codesandbox that you linked to has another issue - you're setting the value to "", but it's not a valid option. In Base UI's Select you have to pass in null if you don't want any options to be selected. An empty string is treated like any other value (and since it doesn't exist among options, the select resets itself to null calling the onChange handler).

michaldudak avatar May 02 '23 19:05 michaldudak

Hello, I have same/similar issue using BaseUI Select with multiple option. When passing some initial values through form (formik initial values), Select takes those values first, but even they are same objects, it fires the onChange event with empty array as new value and resets initial value. Looks like some deep compare is missing in its logic.

little sandbox without form but state init value: https://codesandbox.io/s/hopeful-visvesvaraya-r527yc?file=/demo.tsx

Do you have any workaround till it's fixed please?

I thought about skipping onChange when newValue is empty array, but it's not a good idea, cuz user can also trigger onChange with empty array (when clearing select or taking away last option selected), so it's not possible do it that way.

martsim6 avatar Jun 14 '23 10:06 martsim6

@martsim6 open a new issue, please. I'll look into it.

michaldudak avatar Jun 14 '23 19:06 michaldudak

A simple fix just check if e not null onChange=((e) => { if (e) { // Your code } })

Kmg11 avatar Jan 06 '24 19:01 Kmg11