-
Notifications
You must be signed in to change notification settings - Fork 131
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
Proposal: Namespaces for passing nested data structures #81
Comments
Isn't it wrong to repurpose namespaces for objects? Why not use
Now
<Link x y z inner:href="/" inner:target="_blank" inner:foobar="bizbar" />
<Link x y z inner={{href:"/", target:"_blank", foobar:"bizbar"}} /> The already supported syntax is shorter and also doesn't hide the fact that you end up allocating an object. Your syntax will also favor short names over descriptive names to avoid repetition. It also makes it difficult to refactor when objects become too big, i.e. when you sit there with 20 inner-props and you want to move them out into a shared/external object/helper, you now have to rewrite all of it. If you use the already supported syntax you can just copy-paste or use the entire existing expressiveness of JS to help you generate the object. The syntax opens up for the possibility of redefining the semantics of the generated object (e.g. it is immutable and reusable if constant) which may be an interesting JSX optimization, but I doubt it would be meaningful in the bigger picture and the added user complexity is not to be taken lightly. So personally I fail to see the benefit of this other than "it looks nicer"? |
I really like this. It feels spiritually similar to what happens when you pass data attributes, as they get coalesced into element.dataSet. |
What if my hypothetical 'super components' library wants to have it's special props under 'sc' too? Would styled-components have dibs on sc props? This is why XML namespaces work how they do, with a globally unique URI and a locally unique alias. I don't know how or if this can translate cleanly into the JSX world. |
Its not a matter of dibs, it simply translates |
It looks like the spec supports namespaced tag and attribute names, but Babel says “Namespace tags are not supported. ReactJSX is not XML.” |
Someone else suggested something similar, except with a
|
I'd be happy with a <Button primary css.width="100px" attr.aria-label="Some button">
// Raw props used for cache-busting
const props = {
'primary': true,
'css.width': '100px',
'attr.aria-label': 'Some button'
}
// Some richer object with aliases used for render method
this.props = Object.create(props, {
css: { value: { width: props['css.width'] }},
attr: { value: { 'aria-label': props['attr.aria-label'] }},
}) |
Yeah but it can be added to the JSX spec and then Babel will adjust to support it. |
|
@probablyup I think you might’ve missed the part of the sentence before your quote:
|
What's the process for getting this formalized? Getting it into Babel is the first step I assume? |
I wrote a POF babel transform https://astexplorer.net/#/gist/27b6ec34ebd7036df149229f65bcc235/d8386fe502ceee0e1cb66cdb2b98fdfd26989c90 (it doesn't cover edge cases like spreads etc) fwiw I think that this is redundant and a bit abusing of namespaces :) Also I think that what can be done with a Babel plugin probably should stay in user land. |
Why's that? I strongly feel it's a primitive that the ecosystem would massively benefit from. Also, thanks so much for taking a crack at the plugin code! |
One tweak I might add to this strategy is prefixing generated namespaces with a character so they don't conflict with similarly-named props. <Comp normal="prop" theme:foo={bar} theme:baz theme={fizz} />
createElement(Comp, {
normal: "prop",
$theme: {
foo: bar,
baz: true
},
theme: fizz,
}) Note the |
That would disallow this, though: <div style:color="red" style:border="1px solid black" /> |
How so? <div style:color="red" style:border="1px solid black" />
createElement('div', {
$style: {
border: '1px solid black',
color: 'red',
},
}) And then you could do an inline style if you want too: <div style:color="red" style:border="1px solid black" style={{ marginTop: 1 }} />
createElement('div', {
$style: {
border: '1px solid black',
color: 'red',
},
style: {
marginTop: 1,
}
}) ^ the use of |
Basically a namespace is a different concept than an object - the prop representation is an object, but they're meant to be separate collections of props. |
Why not just do this in some new wrapper/redux functionality: mapEverythingToProps(state, parentProps){
return {
stateProps: state,
parentProps: parentProps,
}
}
const fc = (props) => {
console.log({props}); // {stateProps:{}, parentProps:{foo:'bar', x: 5}}
return <div>hi</div>
}
export default connect<>(
null,
null,
mapEverythingToProps
)(fc); and we render it with the usual:
the difference is that the connect actually intercepts parent props and actually allows you to namespace them. Of course this is more of a redux thing than a react thing. |
This is following on from a chat in person with @gaearon, and I think is different enough from #66 to warrant its own discussion, so here goes:
I'd like to propose that namespaced attributes generate a simple nested group of attributes
compiles to
This to me is a really natural extension of the existing prop semantics, shouldn't conflict with anything proposed in #65 but it also allows a number of nice design possibilities.
React
key
andref
are now no longer simply "special", they're explicit metadata passed to React. It's a fairly big change to React's core API but I don't see any reason to prevent a codemod doing the bulk of the conversion automatically.Styled Components
This is one of the big pain points of designing a library like Styled Components (styled-components/styled-components#439) where we're trying to encapsulate the styling props and the HTML attributes of an element in a single unit. This would make the two types of attributes totally distinct, but without breaking the expressiveness of the component API.
Passthrough props
This comes up a lot (e.g. Hacker0x01/react-datepicker#517 (comment)) where
{...props}
ends up transferring more properties down than is desirable. The above lets the creator of a component explicitly allow "inner" props to be forwarded on.Alternatively, if a
Link
component has a fairly small API and passes on most props, wrapping up all the link-specific props into a single namespace and forwarding on the rest.I don't propose changing anything else about namespaced props, they should work exactly like non-namespaced ones. It simply gives a component author the ability to partition the API into owned and non-owned properties, in a way that's explicit, named, and simple enough that static type inference/checking should be able to be preserved.
What do you think?
The text was updated successfully, but these errors were encountered: