-
Notifications
You must be signed in to change notification settings - Fork 8
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
html-like api vs react-like api for our components #176
Comments
With regards to the Benefits section, I'd like to add the following perspective: The idea behind the current API where we use children wherever possible is that this would produce a very composable set of components. However, in the cases mentioned above (Select / Menu / Transfer), the actual level of composability is more limited than it actually seems, mainly because the parent component makes (implicit) assumptions about the prop-API its children have. One could argue that the above is in fact not an argument against the children-based API, but in favour of good documentation of custom children API requirements for components that clone children and append props... |
I've thought about that as well the last couple of weeks. Whenever I encounter react components not made by us, they usually use a And lately it feels like we've fallen into a jsx trap, which is that we're looking at the code that will be used eventually to generate the dom and see it as html-like, while in reality it's functions and function calls. So from a functional programming perspective (input -> output), passing the data and the view separately should be preferred over mixing the data and view concepts. We can still achieve the same level of flexibility by accepting props that'd replace internally used components (of course only for components that we'd want to be changeable), which would work with web components as well. Of course I'm not advocating for replacing the children prop in general, just for components that have to process their input before being able to display their children. With the Using an options object would reduce the complexity of the |
I think an I worry a little bit about things that can have deeper trees, though, like a menu with possibly multiple sub-menu levels. Could we cleanly support a deep tree of "data" in that case without adding a ton of overhead DSL for the structure of data passed into the component? |
The main problem that I've encountered is when components that are exported separately have some kind of shared state between them. Like the SingleSelect and a SingleSelectOption. In the Select we solved it by cloning the options and adding some props. Context would be another way I assume. If you don't have that shared state, then it should be fine. With the Menu for example, it wasn't an issue until we wanted it to open on click, which meant it needed to keep track of what was clicked (maybe it can be circumvented by a different design btw., don't know). So maybe the core of this issue is that we have certain components that have some sort of shared state/logic (so are coupled in a way), but are exported separately (and you'd expect them to be mostly decoupled). Connecting them is a bit awkward if they're exported separately. Or maybe we just need to find a better pattern for it. |
@amcgee I thought the same, but now I think that - if an app has a complex menu - it probably will have some for of It'd work with custom components as well, as you can just add any property to the config which would be ignored by the default components but can be picked up / used by custom components. So I'd like to add "complex child-tree" to the issue @ismay identified (shared state between separately exported components), |
@Mohammer5 @ismay @HendrikThePendric I think it might be useful to sketch out the proposed prop-based API if we're going to move in that direction? What would this look like for the Transfer, Select, and Menu components? |
Transfer componentCurrent apiThe options are currently provided as label: propTypes.string.isRequired,
value: propTypes.string.isRequired,
additionalData: propTypes.object,
className: propTypes.string,
dataTest: propTypes.string,
disabled: propTypes.bool,
highlighted: propTypes.bool,
onClick: propTypes.func,
onDoubleClick: propTypes.func, The New apiThe transfer component would drop the Transfer.propTypes = {
// all previous proptypes except "children"
options: propTypes.arrayOf(
propTypes.shape({
value: propTypes.string.isRequired,
label: propTypes.string.isRequired,
disabled: propTypes.bool,
// can be used to use a different component for just one option
component: propTypes.any, // any component
})
),
optionComponent: propTypes.any,
}
Transfer.defaultProps = {
// all existing default props
optionComponent: TransferOption,
}
This would allow the app to either replace all or individual options with a custom component, so the flexibility stays the same. |
@Mohammer5's example would also apply to the Select. So instead of: <MultiSelect>
{options.map(({ value, label }) => <MultiSelectOption value={value} label={label} />)}
</MultiSelect> We'd have: <MultiSelect options={options} /> |
Am I correct in assuming that this also allows us to more easily support This seems like a great simplification to me |
The select components already do that. The |
@amcgee We've already made that transition for the SingleSelect and MultiSelect, I don't think it would have been a simpler transition if we used an |
Relates to dhis2/ui#60 The |
Current situation
This is a proposal based on a chat I had with @HendrikThePendric. We were talking about the
<Menu>
component, and how it's a bit awkward that it has some state that it would need to communicate with the<SubMenu>
he was conceptualizing.A similar situation exists for the
<SingleSelect>
and<MultiSelect>
with its options, the<Transfer>
component and its options, and maybe other ones as well. Now I think we've talked about the above before, but I don't remember creating an issue for it, so I thought I'd create one to centralize the discussion a bit.Benefits
The benefit of the current api is that it's reminiscent of how you'd compose regular html elements. I understand we also try to avoid incompatibility with web components, so we don't block that as a future upgrade path (and I might have missed other benefits).
Proposal
On the other hand, it does create a fair bit of extra complexity, with all the mapping and cloning of children, and the data flow in the components becomes a bit awkward. Where instead of everything flowing from the top component down, data flow is a bit more opaque. It feels less like a react-like flow and more like we're mixing approaches.
So the proposal is to move to an api where instead of specific children, the data is passed to the root component directly via a prop, as a simple object, instead of as react children. So a
Select
could accept anoptions
prop, same with theTransfer
, etc.It'd be a big change, so I know it's quite a thing to propose. And there might valid usecases that this'd block. Nevertheless, it hass come up a couple times, so I thought I'd just document it somewhere, so we can form an opinion and remember why we like it or not.
The text was updated successfully, but these errors were encountered: