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

[Select] Out of range defaultValue causes onChange to fire on mount #7

Open
2 tasks done
DiegoAndai opened this issue Oct 4, 2023 · 6 comments · May be fixed by #541
Open
2 tasks done

[Select] Out of range defaultValue causes onChange to fire on mount #7

DiegoAndai opened this issue Oct 4, 2023 · 6 comments · May be fixed by #541
Assignees
Labels
bug 🐛 Something doesn't work component: select This is the name of the generic UI component, not the React module!

Comments

@DiegoAndai
Copy link
Member

DiegoAndai commented Oct 4, 2023

Duplicates

  • I have searched the existing issues

Latest version

  • I have tested the latest version

Steps to reproduce 🕹

Link to live example: https://codesandbox.io/s/use-select-default-value-triggers-on-change-h5mmmh?file=/Demo.js

Steps:

  1. Create a Select component using the useSelect hook
  2. Provide an onChange callback to useSelect
  3. Provide a defaultValue to useSelect which doesn't correspond to any of the options' values

Current behavior 😯

When the component mounts, the onChange callback is fired with null values for the event and newValue parameters.

Expected behavior 🤔

  • The onChange callback shouldn't fire
  • There should be a warning about the value provided to useSelect being out of range

Context 🔦

Refactoring material-ui v5 Select to be based on the useSelect hook. Hopefully, we can make it so that the refactored version fails gracefully when value = "" or defaultValue = "" as that was the way to deselect all options in v5.

Your environment 🌎

npx @mui/envinfo
  System:
    OS: macOS 13.4.1
  Binaries:
    Node: 18.16.1 - ~/.nvm/versions/node/v18.16.1/bin/node
    Yarn: 1.22.19 - ~/.nvm/versions/node/v18.16.1/bin/yarn
    npm: 9.5.1 - ~/.nvm/versions/node/v18.16.1/bin/npm
  Browsers:
    Chrome: 117.0.5938.149
    Edge: Not Found
    Safari: 16.5.1
  npmPackages:
    @emotion/react:  11.11.1 
    @emotion/styled:  11.11.0 
    @mui/base:  5.0.0-beta.17 
    @mui/codemod:  5.14.11 
    @mui/core-downloads-tracker:  5.14.11 
    @mui/docs:  5.14.11 
    @mui/envinfo:  2.0.10 
    @mui/icons-material:  5.14.11 
    @mui/internal-waterfall:  1.0.0 
    @mui/joy:  5.0.0-beta.8 
    @mui/lab:  5.0.0-alpha.146 
    @mui/markdown:  5.0.0 
    @mui/material:  5.14.11 
    @mui/material-next:  6.0.0-alpha.103 
    @mui/private-theming:  5.14.11 
    @mui/styled-engine:  5.14.11 
    @mui/styled-engine-sc:  5.14.11 
    @mui/styles:  5.14.11 
    @mui/system:  5.14.11 
    @mui/types:  7.2.4 
    @mui/utils:  5.14.11 
    @mui/x-charts:  6.0.0-alpha.12 
    @mui/x-data-grid:  6.15.0 
    @mui/x-data-grid-generator:  6.15.0 
    @mui/x-data-grid-premium:  6.15.0 
    @mui/x-data-grid-pro:  6.15.0 
    @mui/x-date-pickers:  6.15.0 
    @mui/x-date-pickers-pro:  6.15.0 
    @mui/x-license-pro:  6.10.2 
    @mui/x-tree-view:  6.0.0-alpha.1 
    @mui/zero-jest:  0.0.1-alpha.0 
    @mui/zero-next-plugin:  0.0.1-alpha.0 
    @mui/zero-runtime:  0.0.1-alpha.0 
    @mui/zero-tag-processor:  0.0.1-alpha.0 
    @mui/zero-vite-plugin:  0.0.1-alpha.0 
    @types/react: ^18.2.23 => 18.2.23 
    react:  18.2.0 
    react-dom:  18.2.0 
    styled-components:  5.3.11 
    typescript: ^5.1.6 => 5.1.6 
@DiegoAndai DiegoAndai added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Oct 4, 2023
@michaldudak
Copy link
Member

A warning here is a good idea, but I did the resetting to no selected value when an invalid one is provided on purpose. Of course, whether it leads to better or worse DX depends on the use case, so I'd wait for additional feedback before changing it.
For Material UI, would changing "" to null before passing to useSelect be an option?

@DiegoAndai
Copy link
Member Author

For Material UI, would changing "" to null before passing to useSelect be an option?

Yeah 🙌🏼 that works.

I did the resetting to no selected value when an invalid one is provided on purpose

What confused me was that the value change that caused the onChange is "hidden" from the developer. I didn't understand why the value changed; it wasn't changing on my side, so I had to dig into the useSelect/useList code to understand why.

This unexpectedonChange firing with no event and no new value might frustrate developers who need to debug it. In my case, the onChange was bubbling up to the Select's related Input, which was throwing an error as the event came as null.

I think the root of this is that the initial state's defaultValue is not checked against the available options (here), while the selected values are (here). My expectation was for both to be checked.

@michaldudak
Copy link
Member

This unexpectedonChange firing with no event and no new value might frustrate developers who need to debug it. In my case, the onChange was bubbling up to the Select's related Input, which was throwing an error as the event came as null.

I think the root of this is that the initial state's defaultValue is not checked against the available options (here), while the selected values are (here). My expectation was for both to be checked.

Gotcha. Do you think a warning in the console would be sufficient to help debug these issues?

I'm not strictly against not firing the onChange. I just imagine issues complaining that onChange wasn't called despite the selected value changed. I suppose there can be some confusion either way.

BTW, it may turn out we need to support nonexistent values somehow to solve #37, but we haven't investigated possible implementations yet.

@DiegoAndai
Copy link
Member Author

Do you think a warning in the console would be sufficient to help debug these issues?

It would be an improvement, but it would still be up to the developer to connect the "Warning nonexistent value" with the onChange firing.

I'm not strictly against not firing the onChange. I just imagine issues complaining that onChange wasn't called despite the selected value changed. I suppose there can be some confusion either way.

I agree that if the value changes, then onChange should fire.

For the "defaultValue prop is a nonexistent value" case, it makes sense to set the value to null as we're in uncontrolled mode. What is weird to me, in this case, is that useSelect initially returns value = defaultValue, then we notice there is a nonexistent value, so we filter it, and then useSelect returns value = null and onChange is called with newValue = null. If we filtered initially so value = null since the start, then onChange would have no reason to fire. Is this a limitation due to the options registering asynchronously? We wouldn't know if it's nonexistent before the options register.

For the "value prop is a nonexistent value" case, I think it's okay to fire onChange, but it could be improved by documenting that this "nonexistent value prop" event will cause onChange to fire. We could improve it more if we gave the developer a way to differentiate if the onChange fired because of a user's selection or because the value prop provided is nonexistent. So maybe adding a third parameter, reason?

I wonder how this behavior would affect the case of async loading of options: for example, a Select with virtualization, if the user selects a value and then scrolls the listbox, making the selected option unmount, would we trigger onChange with newValue = null?

@samuelsycamore samuelsycamore removed the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Oct 10, 2023
@mj12albert mj12albert self-assigned this Jan 10, 2024
@michaldudak michaldudak transferred this issue from mui/material-ui Feb 27, 2024
@michaldudak michaldudak changed the title [base-ui][useSelect] Out of range defaultValue causes onChange to fire on mount [Select] Out of range defaultValue causes onChange to fire on mount Feb 27, 2024
@michaldudak michaldudak added component: select This is the name of the generic UI component, not the React module! bug 🐛 Something doesn't work labels Feb 27, 2024
@aacevski
Copy link

Hello @michaldudak, I'd like to tackle this however what do you think is the best solution?

Imo not triggering the onChange and showing a warning is fine for me - is there any additional things I need to do?

@michaldudak
Copy link
Member

The Select will soon undergo a severe API change, which may affect its internals. Let's not spend time on fixing its bugs till then (as it may be fixed during the API change).

@atomiks atomiks linked a pull request Sep 11, 2024 that will close this issue
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something doesn't work component: select This is the name of the generic UI component, not the React module!
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants