-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Support JSX-Fragments with custom jsxFactory #20469
Comments
@uniqueiniquity can you investigate how other frameworks emit this to see what we should do to support this? |
I have another use case of generating XML files where including extra tags is not an option. I have no idea how this may be implemented, but the need is great enough for our team that I would be willing to spend some time on it. |
After a brief search, it seems that the only React alternative (out of Mithril, Preact, Virtual-DOM, and Deku) that implements fragments is Mithril, which uses However, I think the key observation is here: preactjs/preact#946 (comment) |
Hi. Also implementing a framework needing a custom tsx implementation supporting JSX Fragments. I think this could be best solved with a compiler option to allow replacing React.Fragment with a user-defined string in the same way that React.createElement is currently replaced using the jsxFactory setting. For the Mithral case: Mithral would just need to add a simple function that calls |
I think this still belongs to the domain of having the whole JSX type system refer to the factory method type and allow the factory method to deal with empty braces however the particular implementation desires. |
My 2 cents on how this could be implemented... ProposalFragment shorthand syntax For example, React would define it this way: declare namespace JSX {
interface FragmentProperty { Fragment: {}; }
} If a custom JSX factory does not implement this interface, using the shorthand syntax would be disallowed. The Fragment type should not be constrained to the While I generally wish type checking was done through the |
@RyanCavanaugh I'm using JSX in a way that it renders native DOM objects, and am currently hitting a roadblock as I'm unable to specify a custom pragma for fragments. |
Another use case: Emotion 10 uses a custom JSX pragma to handle the |
Just a data point: This is an issue for TKO/knockout.js as well. |
I like the proposal from @wnayes (#20469 (comment)), but I would change one thing: if the FragmentProperty is not specified, then call the custom JSX factory function with either empty string or null or undefined. |
Any chance this gets some traction? |
With Preact X out now with Fragment support that's another library to add to the growing list. |
+1 to this |
I am working on adding jsx/tsx support in snabbdom https://github.com//snabbdom/pull/451 Would really love to support jsx fragments too. I think the proposal of calling @RyanCavanaugh if you are okay with above proposal that has been mentioned multiple times. I’d be happy to create a PR. based on developit/vhtml#16 The reasoning being This makes it very simple for jsxFactory: handle null for jsx Factory, a function for a function component and a string for a intrinsic element. |
I've made a fix PR #35392 Copy pasting proposal fix here. Problem:Currently The issue has been open for almost 2 years now. Proposal Fix:based on developit/vhtml#16
I think the proposal of calling jsxFactory(null, null, ...children) is very simple and elegant. The reasoning being <>Foo</> is kinda essentially an element without a tag name. So null seems intuitive, like the way null is passed if attributes/props aren't defined. Rather than adding yet another compiler option and pragma, the jsx functions can simply handle null and call their own custom fragment function if needed. Most implementations usually just return the children as array which a parent call stack frame flattens with existing children. User implementations could do custom things too if they wanted and have full control over how they want to convert Example: export type FunctionComponent = (props: { [prop: string]: any }, children?: VNode[]) => VNode;
export function jsx(tag: string | FunctionComponent | null, attrs: VNodeAttrs | null, ...children: VNodeChildren[]): VNode {
const flatChildren = flattenAndFilterFalsey(children, []);
if (tag === null) { // fragment
return MyCustomFragmentFunc(flatChildren);
}
else if (typeof tag === "function") { // function component
return tag(attrs, flatChildren);
} else {
return createVNode(tag, attrs, flatChildren);
}
} This makes a very simple api for custom jsxFactory functions.
|
update: #35392, supports |
While TS doesn't yet support custom JSX fragment pragma, you can just make it work with the defaults if your JSX builder is named In this case, we use it as: import React from 'dom-chef';
export default <>wow!</>; The drawback is just having a variable named |
@fregantor This solution promotes unclean code, and is quite smelly. It's also problematic in environments where custom JSX is needed and React API is also used (I know some). JSX being born with React is not relevant, just a historical detail. |
You talk about code smell and then mention code that uses React but then JSX is handled by another library. 🤷♂️ I'm just suggesting a workaround at the moment, of course if there's proper solution I'd avoid having |
Any framework which builds upon react as a rendering engine (don't ask for example), can get in such a situation, nothing smelly in that. |
Any update on this? Btw. Guys. If I don't know if know about it, but there is a workaround for import { h, Fragment } from "preact"
export const Component = () => (
<Fragment>
// anything in here will not be nested
</Fragment>
) |
I’ll make another pass on it.
|
This issue has not been resolved for a long time since the question was asked. |
Any updates on this? 🙂 |
One thought I had just now, what if we transpiled I think either null or an empty string would be strictly better because it would mean we reduced the number of config options, and that frameworks themselves can figure out what to do with the syntax for themselves. I also think an empty string is the most natural way to interpret JSX fragment syntax. |
It’s going to be hard to gain support for either null or empty tag. Mostly because the major frameworks all provide a Fragment function like React/Preact. Babel supports jsxFrag pragma. I guess one can always set “jsxFragmentFactory: ‘’” and get what you’re proposing. However it makes sense to support the compiler flag to tsc is consistent with Babel and gives user the flexibility to choose rather than forcing its opinion. |
Most frameworks could probably support factory calls with empty strings meaning fragments very easily, we’d all just have to agree to it.
Ehh. We should always be looking for opportunities to lighten the configuration burden we impose on our fellow developers. There is no situation where you have a
I agree it would be nice to have consistency with Babel. |
I strongly disagree with this.
I love that you're trying to find ways to simplify cognitive load for fellow developers, but I'd encourage you to look more closely why we're in this "mess" in the first place. The reason we're in this situation is because facebook has traditionally used their own bundler (I think it's called haste?) and published React only as a CommonJS module. When ES6 started to become a thing many imported React like a default import: // react
module.exports = {
createElement() {...}
}
// my-app.js
import React from "react";
const div = <div /> After transpilaton: // my-app.js
import React from "react";
const div = React.createElement("div", null); In other words: The If we'd live in a separate timeline we wouldn't have that problem in the first place. import { createElement, Fragment } from "my-cool-framework";
// const div = <div />;
const div = createElement("div, null); But we don't live in that world. We have to deal with |
@marvinhagemeister I believe that you have a strong argument about this messy situation being caused by early days' decisions and React being so predominant back then. Some decisions around JSX support would look quite different if implemented for a more general use case, but many have been made with React alone in mind. It seems to me though that maybe you are in a good position to help to shape the alternative timeline now - by focusing on a good JSX implementation/semantics for the upcoming "auto" mode. IMHO it has the potential to act a clean slate for a couple of things and most likely could solve some of your pain points in one go. |
I blur the lines between host and component elements in Crank a little bit by using Symbols as an alternative to strings. JSX has that tricky limitation where uppercase means identifier and lowercase means string, but if you also make it possible to use symbols, you can import and use uppercase tags which work like host nodes. This makes it easier to support case-sensitive xml in custom renderers. So in Crank (and actually React too), Fragment is a symbol and not a function. I’m also trying to get Typescript to support this pattern here #38367, maybe you have a strong reaction to this too? Maybe it’s a bad idea, but I also thought there were too many disanalogies to saying a Fragment is a component.
Correct me if I’m wrong, but couldn’t you just add a check somewhere around here for an empty string and reassign |
developit/microbundle#623 A known issue in TypeScript: microsoft/TypeScript#20469
* Use Babel plugins to fix Fragments issue developit/microbundle#623 A known issue in TypeScript: microsoft/TypeScript#20469 * Remove default mention. It's not accurate here
Hi. I’m wondering what the reasoning was for explicitly disallowing non-identifiers? I’ve opened an issue (#41400) for the use-case of literals, specifically, the empty string, being used as fragment syntax. A cursory read through the related PR indicates this restriction is mostly artificial but I may be mistaken. |
Really love the new JSX-Fragment syntax. It's great 💯
I was wondering if we could enable it for custom
jsxFactory
in the future. I'd really love to use this feature outside of react 👍Use cases
Basically any non-react framework that works with jsx (mithril, preact, custom ones...).
Current workaround:
The text was updated successfully, but these errors were encountered: