Controlled and uncontrolled
Most components' "current value" can be held by you (controlled) or kept by the component itself (uncontrolled).
Three channels
For the option data contract shared by the select family (value, group headings, labelText, disabled propagation, etc.), see Selection and options.
How control is decided
Passing value (or checked) makes it controlled, and the component renders exactly the value you pass; if you don't pass it, the component keeps its own internal state, with defaultValue used only as the initial value.
Don't flip value back and forth between a value and undefined — that's a mode change, which isn't supported.
Callback signatures
Value callbacks are uniformly value-first:
Most cases only use the first argument (onChange={setValue} is enough). The second argument is the native event that triggered the change, and is only provided by the input components.
Callbacks that don't follow this signature:
Two "choose" callbacks that are easy to confuse:
- The
Selectfamily offers bothonChange(the selected value changed; re-selecting the same item doesn't fire) andonChoose(fires on every choice, including repeats). WhenclearOnChooseis true, onlyonChoosefires and the selected value doesn't change — handy for command menus. SearchWidgethas noonChange; query changes go throughonQueryChange.
Dialogs must be controlled
Dialog, MessageDialog and ProcessDialog only have open (required), with no defaultOpen: opening and closing are driven by your state, and close actions notify you through callbacks.
MessageDialog also has onOk / onCancel, and ProcessDialog also has onAction; see their component pages.
When the controlled value isn't among the options
The value you pass in sometimes matches no entry in options, for example when an option is removed, the initial value is wrong, or the options load asynchronously and aren't there yet on the first render. How components respond falls into two groups:
For the first group, suppose options are a and b but you pass value="c":
The component shows a (the fallback) and calls onChange("a") to tell you "this is what actually took effect". Update your state to a and value and the UI line up again. The same invalid value is written back only once, so ignoring the callback won't cause it to fire repeatedly.
The second group follows your value exactly: no match means nothing is selected, the UI honestly reflects "no match for the current value", and you decide whether to correct it yourself.