Closed dzearing closed 3 years ago
@dzearing can you please add to each proposal an example for signature like this:
onChange: (ev: React.FormEvent, data: TProps)
Currently, I don't understand difference between options.
@layershifter done;
A: move props into a member of data B: spread props into data C: data is just the new value
A is the easiest to extend as needed in the future, similar to how HTMLEvent
is architected, where the event arg data contains not only the element but a much of metadata about the origin and context of the change. We likely will need this sort of context in the future as well. For example, a RadioGroup
value
may be the RadioGroupItem
that was selected, but the index of the selection may also be required, so that would be additional metadata on the data
object we could add, without worrying how it affects props
mixed in.
Because this issue has not had activity for over 150 days, we're automatically closing it for house-keeping purposes.
Still require assistance? Please, create a new issue with up-to date details.
Overview
We should provide guidance on how
onChange
events are exposed across components.Today we have a discrepancy between v0 and v7 components:
Use cases for
onChange
The primary data has changed, and I need the new value to support controlled component cases or to forward the data to a state management system.
When the data changes I not only need the new value, but I need the original props of the component, so that I can differentiate which instance fired the change event to a general handler.
There are a few types of components which might fire an
onChange
event:Input
andToggle
RadioGroup
,Select
Dropdown
,DetailsList
,Tree
Prior considerations
Semantic UI's thread for
onChange
which provided some background on thinking for Option B below: https://github.com/Semantic-Org/Semantic-UI-React/issues/623#issuecomment-261018287Material UI follows a similar approach to v7 code: the second argument represents the new value being changed. See Select API as an example of this. However the components aren't consistent - See Input API or Switch API where neither have a second argument.
Proposals
Option A (recommended):
data
object where we provide the following minimum props:value
props
Example:
Pros
π value is always predictable in the
value
prop π Parent component's current props are accessible and don't overlap other things in the data object π Future proof; data object is extendable without ever accidentally overriding a potentially needed prop value. π Type safety is simple.Cons
π
data.props.value
anddata.props.defaultValue
are accessible, leaving multiple ways to see value which might be confusing as they'll represent the current props rather than the new value. (But it should be obvious that these are user inputs and not the new value. (Could consider calling new valuenewValue
to be clear, but that seems a bit unpredictable.)Option B: stick with v0 approach -
data
is{ ...props, value }
Example:
Pros
π Can access parent props mostly (unless it was overridden by some selection data.) π One copy of
value
(defaultValue
could still be there.)Cons
π Mixing additional meta data about the change (such as index or virtualized selection information) runs the risk of overlapping on the parent's props. (e.g.
index
prop of the parent vsindex
of the new selected item) π Slightly less efficient (copy all props over vs just set theprops
value)Option C: stick with v7 approach - second argument is the new value
Example:
TextField
usesstring
for thenewValue
type.Toggle
usesboolean
for thenewValue
type.ChoiceGroup
usesIChoiceGroupOption
asnewValue
type.Pros
π Main use case of getting new value is straightforward π No breaking changes with v7, less churn for customers
Cons
π Can't access
props
of parent component from callback, so to do things like sharing one callback implementation which maintains a form hash table requires accessingev.target.id
to know which component changed.π Adding additional information to the data like
index
orkey
is a breaking change.