Skip to content
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

Alternative syntax for passing JSX content to props #62

Closed
danielearwicker opened this issue Sep 14, 2016 · 4 comments
Closed

Alternative syntax for passing JSX content to props #62

danielearwicker opened this issue Sep 14, 2016 · 4 comments

Comments

@danielearwicker
Copy link

Silly (but succinct) example:

const If = ({test, yes, no}) => test ? yes : no;

Today we can say this:

const eg1 = (
    <If test={allowed}
        yes={
            <button>Launch missiles</button>
        }
        no={
            <span>Access denied</span>
        } />
);

Proposal is to support this alternative, inspired by XAML:

const eg2 = (
    <If test={allowed}>
        <If.yes>
            <button>Launch missiles</button>
        </If.yes>
        <If.no>
            <span>Access denied</span>
        </If.no>
    </If>
)

In short, if an element name contains . and the first part is the parent element's name, the second part is a prop name and the content becomes the value passed to that prop. Nothing else changes - it's merely an alternative way to specify the prop value.

It remains consistent with XML, where . is allowed; indeed, XAML was forced to do this in order to be an XML dialect and yet be flexible about how properties are specified on objects. JSX doesn't have that challenge, nevertheless in more complex examples it would lessen brain- and eye-strain to keep the structure of the tree obvious by staying in JSX syntax instead of { ... } blocks stretching across many lines, and with a few layers of nesting alternating syntaxes.

And a further enhancement, say we have:

const For = ({items, each}) => <div>{items.map(each)} </div>

Today we can say this:

const eg3 = (
    <ul>
        <For items={things} 
            each={item => (
                <li>{item}</li>
            )} />
    </ul>
);

But how about:

const eg4 = (
    <ul>
        <For items={things}>
            <For.each item>
                <li>{item}</li>
            </For.each>
        </For>
    </ul>
);

That is, in these "prop elements", which have no other purpose for their attributes, they can optionally specify valueless attributes. These become the parameter names of a function, and that function is then is passed to the prop. (Once again, it maps exactly to the previous example.) If no attributes are specified, as in eg2, then there is nothing variable for the content to depend on and hence it can just be a simple value rather than a function returning a value.

I found a prior issue facebook/react#848 that seemed to be asking for something similar but wasn't as precisely mapped to existing concepts. Here I'm not talking about anything that changes the model, just a way of staying in JSX and reducing the mental overload of switching syntaxes when writing/reading trees.

@sophiebits
Copy link
Contributor

Your eg2 is already possible in React if you write the If component cleverly.

Your eg4 example would need to change the semantics of JSX pretty drastically because currently every subexpression is eagerly evaluated and there is no way to represent this currently in JSX. I think @jimfb had a proposal somewhere (which we're unlikely to take) but I can't find it now.

@danielearwicker
Copy link
Author

Yes, @zpao told me how a component could dig down into grandchildren. Sounds scary and wrong!

Regarding eager evaluation, that means I should correct this:

If no attributes are specified, as in eg2, then there is nothing variable for the content to depend on and hence it can just be a simple value rather than a function returning a value.

Instead, my If example should be:

const If = ({test, yes, no}) => test ? yes() : no();

i.e. yes and no are functions, and so eg2 should generate nullary functions, being equivalent to:

const eg1 = (
    <If test={allowed}
        yes={() => <button>Launch missiles</button>}
        no={() => <span>Access denied</span>} 
    />
);

i.e. what I've specified here is purely syntactic sugar, a first-pass transformation based on a pattern that can be found in element names. It would "compile down" to combinations of JSX and {(...) => ... }-bracketed chunks, with behaviour exactly as supported today, as in the above examples.

@sophiebits
Copy link
Contributor

What you have is already valid syntax though (which means something different than the compilation you suggest). I don't think we're likely to support something like this in JSX itself but you should feel free to make your own syntax extension if you think it's valuable. Thanks for sending the idea!

@danielearwicker
Copy link
Author

Ah, I see . is already supported and just passed through. : would be a possibility though.

Really I'd like to see this in TypeScript, and it's very unlikely to be adopted there unilaterally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants