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

[Suggestion] Remove setting value: '' for all input fields #336

Open
ktmud opened this issue Mar 10, 2020 · 7 comments
Open

[Suggestion] Remove setting value: '' for all input fields #336

ktmud opened this issue Mar 10, 2020 · 7 comments

Comments

@ktmud
Copy link

ktmud commented Mar 10, 2020

Not sure if this is intentional, but I see here partitionFieldProps sets value = '' for all form fields every time they render:

inputProps: {
value: '',
...inputProps,
disabled,

This basically makes an input unusable without a state bind to input.value. Even the most basic input example in the Storybook does not work (users cannot type anything).

It's generally a bad practice to write what users typed right back into an input. It triggers an unnecessary re-render, increases the risk of circular callbacks and greatly increases code complexity. For example, a PR in Bighead had to introduced a new state just to use a debounced onChange handler.

Code as simple as

export default function DebouncedInput(props: DebouncedInputProps) {
  const { onChange, debounceTime = 150, ...restProps } = props;
  const handleChange = debounce(onChange, debounceTime);
  return <Input {...restProps} onChange={handleChange} />;
};

had to change to

export default function DebouncedInput(props: DebouncedInputProps) {
  const { onChange, debounceTime = 150, ...restProps } = props;
  const [value, setValue] = useState(restProps.value);
  const handleChange = debounce(onChange, debounceTime);

  return <Input {...restProps} value={value} onChange={(
    newValue: string,
    event: React.ChangeEvent<HTMLInputElement>
  ) => { 
    setValue(newValue);
    handleChange(newValue, event);
  }} />;
}

And most importantly, manually setting user input as users type has the side effect of interfering with other native browser behaviors, breaking things like autocomplete and foreign language input (I've seen such bugs many many times).

In case a clean input is desired, developers should provide value="" themselves.


The fix is as simple as remove value: ''. If this change would break current developer expectations or other features, it's the best to at least provide an option to disable this behavior.

@schillerk
Copy link
Collaborator

It's generally a bad practice to write what users typed right back into an input.

Do you mean controlled components are bad in general?

Normally, in the Bighead example, the parent component DeployModal would pass it's own state value search down to the Input field. In this case, because it's Debounced, we want

  1. Debounced state value in DeployModal
  2. Instant state value in Input

But that seems like a very specific case right? Outside of debouncing, it's hard to think of examples where an uncontrolled component would be preferable. I'm sure they exist, but this seems like an exception, and not a general rule.

@ktmud
Copy link
Author

ktmud commented Mar 11, 2020

Do you mean controlled components are bad in general?

I actually wasn't aware of controlled components. Thanks for calling it out. My comments mostly came from my experience pre-React era. There were just so many nasty bugs I have dealt with that originated from setting input values as users type.

I agree controlled components can be helpful for most cases, but IMO developers should still have the option to not use it. For simpler form fields, there might not be even a need to use states. Setting value='' took that option away.

@ktmud
Copy link
Author

ktmud commented Mar 11, 2020

Btw, sorry if I sounded more forceful than I actually was. This just reminded me of some very awful bugs.

@schillerk
Copy link
Collaborator

Haha, no problem.

At least for DX, our UIs almost always adapt to user input in some way, as opposed to having uncontrolled input that we only read on submit. For example:

  • Live search/filtering
  • Autocomplete
  • Field validation

Aside from debouncing, I really struggle to think of a good use case.

FWIW, other component libraries do something similar:
https://github.com/ant-design/ant-design/blob/master/components/input/Input.tsx

One downside is that not having a default value allows users to change between a Controlled and Uncontrolled component. I.e. Value starts out undefined, but is later set by the parent component, resulting in:
Warning: Input is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component

Since React generates an error anyway, I'm not super concerned about this. I don't have a strong opinion either way.

@stefhatcher
Copy link
Contributor

For debouncing, it's rarely done in handlers. Debouncing is done on API calls, and those are usually throttled instead, outside of the presentational component. It's app logic. I disagree with removing it. The form inputs from the form package handle default values, setting the values, and the flexibility to change them with callbacks like onBatchChange.

@ktmud
Copy link
Author

ktmud commented Mar 11, 2020

@schillerk Ant design does not reset value to '' every time the component rerenders.
See https://codesandbox.io/s/antd-input-vs-lunar-input-oo7tn

IMO this value always resets to '' behavior would be a surprise to most developers. setState should not change where the state is not being used.

If we really want to enforce controlled components and keep this reset, maybe make value a required prop so future developers can feel less surprised.

@milesj
Copy link
Contributor

milesj commented Mar 11, 2020

All form components in core are controlled components, and this is the pattern in which controlled works. Resetting value to an empty string isn't necessarily correct, as the controlled components require the consumer to pass a value in to work. The empty string is just the default state.

Removing that empty stirng is a breaking change, and we just released v3, so this wont happen anytime soon. However, we can probably control this through a prop (like uncontrolled), coupled with partitionFieldProps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants