Coder Social home page Coder Social logo

Comments (47)

matthewwithanm avatar matthewwithanm commented on May 3, 2024 9

We can argue all day long about the merits of either approach, but most developers would eventually opt for the second approach if given the choice.

This might seem natural for people who are super used to Angular but personally, at least, I'd be pretty bummed to see concepts like conditional expressions and greater-than comparisons reimplemented in JSX. Part of what I love about it is that it doesn't do that.

from react-future.

matthewwithanm avatar matthewwithanm commented on May 3, 2024 8

@matthewwithanm We all love JSX! Can you describe more what would make you sad? It may be possible to describe some definition of an attribute directive that wouldn't corrupt JSX in your view.

The thing I love about it is that it doesn't introduce a bunch of new concepts. Describing JSX now is basically like "It's a way of representing something that's going to be rendered. The attributes are passed to the component as props." That's it. It's basically as complicated of a jump as the one from arrays to array literals. What if you want to create one conditionally? Well, you do the same thing you'd do if you wanted to create an array conditionally. "It's Just JavaScript." 😊

Maybe a directive could simply be an observer listening to changes on the component and intercepting the output of the render method. Or something like that, instead of a full blown component. Or maybe the attribute directive could be a class decorator of sorts.

That's even more concepts…

Attribute directives are one to one translatable to components. Full stop.

So you explain to somebody that <Thing *Flashing /> is another way of writing <Flashing><Thing /></Flashing>. But then they want to pass props to these attributes so we need another syntax for that. Maybe it's something like in your <Thing *Flashing *If={true} /> example. But wait, what prop is that condition? Is there some kind of default prop syntax? Also, oops, did I just write something that means <If={true}><Flashing><Thing /></Flashing></If> or <Flashing><If={true}><Thing /></If></Flashing>? That seems like an easy mistake, maybe we need a special way of prioritizing regardless of order? You just end up layering syntax on top of syntax that's separate from JavaScript, and accomplishes something that can already be done but in a less explicit, less clear way.

I'm really interested to hear your thoughts. Is there another solution we could find to make React more friendly and inclusive? 75%+ of the dev world have only ever written HTML and attribute directives would make their transition to React much smoother.

Sorry, but I don't buy the premise. 😞 What makes you think that there's a majority of people writing attribute directives? In my opinion, making React more friendly and inclusive means introducing fewer new concepts—not more.

As long as it doesn't hurt performance or the simplicity of React/JSX?

It does hurt the conceptual simplicity though. You need to know more stuff that isn't "just JS."

There's a good portion of the dev world who wants their HTML to look like HTML and if we can, I think React should try to appeal to them as well.

I'm confused about this one. Attribute directives don't look like HTML.

I think we need to be careful to not fall victim to group mentality where we define ourselves as a community by our differences with other frameworks like Angular, but try to stay as open and as inclusive as possible.

I also don't get how inclusivity is an argument for any particular feature from another framework. Would adding scopes or dependency injection make React more inclusive? I know to somebody who's knee-deep in Angular, it might feel that way but, again, I think the way to real inclusivity is introducing fewer concepts you gotta learn and making sure that each one "pays its way." This one imo doesn't.

from react-future.

sophiebits avatar sophiebits commented on May 3, 2024 6

Hi @scottmas – I don't want to get involved with the discussion here but please don't state claims like:

However, it is objectively true that many, many more developers in the world will be able to more rapidly understand the first.

as fact when this neither backed up by evidence nor something that everyone in this discussion would likely agree on. Looking forward to continuing to watch this discussion as long as it doesn't get heated.

from react-future.

pluma avatar pluma commented on May 3, 2024 5

This entire argument seems to be based on a disagreement on a single issue:

JSX is an extension of JavaScript, so in order to learn JSX you should first understand basic JavaScript and in order to become better at JSX you should become better at JavaScript.

The argument seems to be that because JSX looks kinda like HTML which can be understood without understanding JavaScript, it should also be possible to read and write JSX without understanding even basic JavaScript.

The one issue this argument completely overlooks is that there is no way to have logic in JSX without requiring some knowledge of something other than basic HTML. The proposed additions extend JSX with custom proprietary non-HTML syntax. This is pretty much the route template languages like Angular2's take. Naively this seems like an easy fix because "at least you don't have to learn JavaScript" but it brings with it all kinds of problems:

  • everybody has to learn a completely new language nobody already knows
  • nobody has used the language before so there are no third-party resources dedicated to it
  • the language can't make any use of existing JavaScript code without additional complexity and indirection
  • authors need to context-switch when writing the language if they are already using JS (this is in addition to the context-switching for HTML itself)
  • even when used correctly there might be subtle differences in semantics with nearly identical JS

If we're just talking about extending JSX without making it a proper template language we still run into problems with evaluation semantics: conditions in if statements and boolean expressions are evaluated using short-circuiting which makes things like (x && x.y && x.y.z) possible (even when x is undefined). By nesting boolean expressions you can avoid instantiating components you're not going to use or for props you won't actually have. This is a language feature of JavaScript itself.

In order to make stuff like *If work you would have to avoid creating the child component instances until the expression is evaluated. Creating the child component instances ahead of time would make it useless. However changing these evaluation semantics would potentially be even more confusing because it's additional semantics newcomers would have to learn, making JSX even more complex.

from react-future.

KyleAMathews avatar KyleAMathews commented on May 3, 2024 4

@jamesmfriedman or make a custom Input component <Input type="text" validate-email="multiple" validate-min-length="5" validate-has-chars="@mydomain.com" />

from react-future.

lettertwo avatar lettertwo commented on May 3, 2024 3

I think the difference of opinion here might center on two different interpretations of what JSX is for. (I don't want to put words in any of your...keyboards, so correct me if i'm not on target with any of this!)

One idea of JSX is that it is a way to express DOM (or, if you squint, native elements). From this perspective, it makes a lot of sense to strive to make your JSX as close to the 'metal' as possible. (i.e., you might genuinely want to know that what you're rendering is a <div *Thing *Flashing /> rather than a <Flashing><Thing /></Flashing>).

Another idea of JSX is that it is a way to declare a function composition. From this perspective, <Flashing><Thing /></Flashing> translates to Flashing(Thing()), which is a perfectly decent way to compose a flashing behavior into a thing.

I believe that the second idea of JSX is what makes concepts like attribute directives or logic/flow control components seem less compelling.

Admittedly, the first interpretation of JSX is much closer to what many React devs are actually doing with React (rendering DOM), but i think there is a lot of elegance and power in React (and JSX) that is lost in the details of trying to mold JSX into an HTML-like template language.

Though the first interpretation does jive with the most common product of working with React (though less so for non-DOM render targets), I think the second interpretation is more in line with what many React devs are thinking when working with React, which ultimately matters more to the developer experience, both short- and long-term, IMO.

from react-future.

lettertwo avatar lettertwo commented on May 3, 2024 3

But have they ever added a class to an element to hide it? If so, they already understand how this works:

 <div *Hide={someVar}>Hi</div>

Whereas they would look at the following in incomprehension:

{someVar && <div>Hi</div>}

I'm not sure that someone who only knows HTML would understand how the first form works
because it would work exactly how this works:

<Hide><div /></Hide>

Which is analogous to function composition, an entirely different operation from function application via props or attributes.

So while it may look syntactically similar to an html element with attributes, it is semantically very different.

I think that a React dev coming to understand the second form is exactly the point of emphasizing that JSX is 'just javascript'. If JSX adds it's own programming meta language on top of javascript, then it becomes much tougher to justify JSX as 'just javascript', because it will have become a subset of JS (i.e., a template language).

from react-future.

satya164 avatar satya164 commented on May 3, 2024 2

You don't need a else in the block. I think for tiny expressions, ternaries work fine, or even bool && result work.

Anyway, there have been already a lot of discussions in #35, and it is still open. This issue is a duplicate of that one. Please discuss there instead.

from react-future.

lettertwo avatar lettertwo commented on May 3, 2024 2

IMO, JSX semantics don't need to be (and shouldn't be) coupled to html semantics.

I.e., it's not particularly useful or important that a JSX expression look like:

<div *Column>
  <ul *List>
    <li *ListItem>
      <a *Link href="#">click me</a>
    </li>
  </ul>
</div>

However, it is useful and important that it looks like:

<Column>
  <List>
    <ListItem>
      <Link to="#">click me</Link>
    </ListItem>
  </List>
</Column>

This illustration may not be convincing if you're used to thinking of React as a way to render DOM, but consider the case of react-native. Things like <ul> and <div> no longer have any semantic meaning when your render target is native, but things like <List> and <ListItem> certainly do!

from react-future.

scottmas avatar scottmas commented on May 3, 2024 2

Really I just need to stop talking about this and create a babel JSX plugin!

from react-future.

pigoz avatar pigoz commented on May 3, 2024 2

JSX is easy, but not simple.

I am not sure if you intended to quote Rich Hickey, but simple and easy are very loaded adjectives in our field and IMHO you got definitions wrong.

Compared to Angular's attributes JSX is the simpler technology: it has few, well defined semantics that are not surprising and do not require too much to explain. OTOH Angular's attributes require a lot of explaining, and allow you to generate something that is complex with fewer keystrokes, that makes them easy, not simple.

from react-future.

sophiebits avatar sophiebits commented on May 3, 2024 1

We can leave this one open.

We've thought it's more compositional and easier to understand if you break that into separate components (ex: a flexbox component) but perhaps there's value here worth discussing.

from react-future.

syranide avatar syranide commented on May 3, 2024 1
<div *If={true} *Bold *Flashing>Hello World</div>

vs

{true && <div><Bold><Flashing>Hello World</Bold></Flashing></div>}

vs (with implicit do)

{if (true) <div><Bold><Flashing>Hello World</Bold></Flashing></div>}

Comparing apples to apples, it doesn't seem like that a significant win considering the cost. The only immediate win I see (if I understand it correctly) is that it makes it faster to nest as you don't need to close off the tags as well.

But wouldn't a syntax like this make more sense and be more JSXy for easy nesting then? (or some variation of it)

{if (true) <div <Bold <Flashing>>>Hello World</div>}

thus

{if (true) <div foo="bar" <Bold <Flashing bar="foo">>>Hello World</div>}

from react-future.

brigand avatar brigand commented on May 3, 2024 1

Minor point for any of these: <div>{x.length && <span />}</div>. Not sure how many times I've done this by mistake 🥇

from react-future.

satya164 avatar satya164 commented on May 3, 2024 1

I haven't talked about passing props to attribute directives yet, but would a JSX transform of the following really be so incomprehensible and complicated?

It does look confusing to me. <div label="title" /> and <div *div={title} /> seem completely different from each other. If you ask me, devs who have only written HTML will find this more alien than just writing nested tags like they do in HTML.

from react-future.

scottmas avatar scottmas commented on May 3, 2024 1

I'm not sure I understand you fully @satya164. I'm not saying there's no conceptual leap at all, just that there's less of a conceptual leap from plain html <div class='hide'/> to <div *Hide={true}/> then there is to {true && <div/>}. At least to me, this seems relatively self-evident, although I haven't conducted a formal poll.

And I am sorry @spicyj, I didn't mean to make divisive statements. I genuinely did think everyone would agree with that statement. Not that it's a better way, but that it's a less of conceptual leap for devs familiar with html.

from react-future.

scottmas avatar scottmas commented on May 3, 2024 1

Hey Pluma, I've seen your work on ArangoDB before. It's a nice looking database.

I think our disagreement is mostly a matter of degree though. With the current proposal, *If would work in exactly the same way as <If condition={false}>Hi<//If> would work. Which would not work with an undefined variable. The good news is that with this type of nested component implementation, your concerns about further complicating the evaluation semantics would be unfounded. The babel plugin would be relatively simple, and not break any existing React code.

But like I said, it's a matter of degree.

On one end of the spectrum, there's a lot of React purists who loathe and despise JSX. They want pure functional goodness in their UI's, without JSX sugar obfuscating the actual React function calls. In their view it makes things simpler and cleaner. And they are entitled to their opinion. With syntax sugar like JSX, there really is no "right" answer.

Somewhere in the middle, we have JSX as it currently stands. Relatively transparent mapping from <div/> to React.createElement('div', ...). It's brings some of the visual niceties of HTML to Javascript. Developers are forced to learn Javascript, not some DSL. This happy combination of easy on the eyes structure + just needing to know javascript is one of the original reasons I fell in love with React. I don't want to change that. I like it.

A little further on the spectrum is this proposal. We add some attribute goodness to JSX, with a relatively transparent mapping from <div *Attr/> to React.createElement(Attr, null, React.createElement('div', null, null)). It makes the mapping from JSX to Javascript a bit more complex, but not by much. For people like me, I feel like it keeps the pure Javascript functional goodness, but just adds a little more JSX'ey goodness on top. In the same way that JSX itself makes raw Javascript a little easier to work with. In my view, some components are better thought of behavior modifiers, rather than structural components, and would be better to write as attributes rather than nesting parent components.

At the extreme end of the spectrum, we have frameworks like Angular. Lots of people love it. I don't. It becomes this hugely complicated DSL with a million lifecycle hooks and triggers, and it becomes very hard to follow what's going to happen next. Suddenly you need to become an Angular developer, not a Javascript developer.

So basically what I'm saying, is that this proposal would add a little more syntax sugar on top but not at all change the fundamental nature of JSX. It would still be just Javascript. There would just be one more option that people like myself would find extremely useful and easy to work with.

from react-future.

scottmas avatar scottmas commented on May 3, 2024 1

But this thread is exhausting me. I feel like JSX has become such a part of so many React devs identity, that a pretty big proposal like this (even though backwards compatible and requiring nothing more than a babel transform) just hits someplace deep.

So closing this for now. Maybe it'll be worth opening if I or someone else create a babel plugin proof of concept and we can discuss relative merits, etcetera, more concretely.

from react-future.

syranide avatar syranide commented on May 3, 2024 1

@scottmas IMHO none of those seem sane. The syntax might look nice, but it seems like you're abusing the concept of components. This can and should be accomplished using plain functions. It's flexible, reusable and accurate. Because the same validator can now also be used before submitting to be doubly sure that values are OK, rather than somehow relying on storing validator results in state.

let emailValidator = createValidator([validatorMinLength(...), validatorEmail(...)])
// ...
let validateResult = emailValidator(this.state.email);
return (
  <div>
    {validateResult ? <ValidateWarning result={validateResult} /> : null}
    <input type="text" ... />
  </div>
);

That's just POC and much more flexible. You can stick it in your own flexible component if you like to make it easier to reuse. There are lots of ways to do this.

So it could look like this:

<MyInput validator={emailValidator} ... />

And you could take it even further if you're doing forms on a large scale:

let fields = [
  {name: 'foo', type: 'blah', validator: blahValidator},
  {name: 'bar', type: 'email', validator: emailValidator},
];
<MyForm fields={fields} onValidSubmit={...} ... />

Again, this is just what I've come up with on the spot. There may be tweaks or entirely different ways that are more suitable for various purposes. But the point is that using the existing tools you can accomplish the exact same thing you're trying to invent a new syntax for, but using more sensible runtime features (nesting validator elements does not seem sane). The best solution depends entirely on how much convenience vs flexibility you want.

from react-future.

natew avatar natew commented on May 3, 2024

This is not fully related, but you can implement stuff similar to this with transforms.

We do this with gloss (see all the $props). Example usage:

image

And for custom properties that do logic, we made babel-jsx-if, so you can do if={bool}:

image

from react-future.

scottmas avatar scottmas commented on May 3, 2024

Nice usage of transforms! I have investigated babel transforms a bit to jimmy rig attribute logic, but the problem is that Babel transforms are the wild wild west of React. Literally anything is possible using them. What we need is something standardized so that everyone can develop on the same page.

I guarantee attribute directives would be highly used by developers in the wild if a spec for them could be created.

As a possible spec, I imagine it would be easiest if attribute directives simply created wrapping React components on the fly (e.g. it wrapped the original component), and had the ability to pass through the original component unmodified.

Maybe something like this:

class RandomHide extends React.AttributeDirective{
  constructor(){
    super();
    this.state = this.getRandomState();
    window.setInterval(() => {
       this.setState(this.getRandomState());
    }) 
  }
  getRandomState(){
    return {
       hide: Math.random() > 0.5
    }
  }
  render(origElm){
     return this.state.hide ? null : origElm;
  }
}

from react-future.

satya164 avatar satya164 commented on May 3, 2024

why not use do expressions?

from react-future.

scottmas avatar scottmas commented on May 3, 2024

With do expressions we still have a pretty undesirable developer experience. Contrast this:

  {do {
      if(true) { <span>'hello world'</span> }
      else { null }
    }}

with this:

<span *jsxIf={true}>Hello world</span>

The second approach is way more readable. Do expressions can be nice for complex conditional logic, but still don't get us where we want to be.

I'm not making an argument that alternatives to attribute directives don't exist, because there are lots of them: babel transforms, do expressions, plain ol' React components, etc. But each of these options are either verbose (e.g. plain ol' React components and do expressions) or wild, wild west (e.g. babel transforms)

from react-future.

scottmas avatar scottmas commented on May 3, 2024

My point is far more general than #35, although I am very interested in the conclusion to that discussion. Of course we can still use bool && <div>hello world</div>, but the larger issue I'm raising is why React has zero support for custom attribute directives, which are a cornerstone of Angular and many frontend frameworks.

Are we really so confident that: (1) attribute directives are not useful, and that (2) it would be too breaking to implement them in React/JSX? Until we have a discussion, I don't think we know the answer to either of these questions.

Attribute directives are dang handy, and NOT just for conditional expression. See for example the proposed syntax for an Angular like Flexbox attribute implementation:

<div *fxFlex={33} *fxFlex.gt-sm={67}>Hello world</div>

With all due respect, this issue should be re-opened. @satya164

If attribute logic were to be integrated into React, it could make the whole question of conditional JSX expression solveable in user land instead of in the JSX core specification. And this is just one example of the type of problems that attribute logic lends itself to solving.

from react-future.

brigand avatar brigand commented on May 3, 2024

Alternative syntax:

const fxFlex = (el, value) => React.cloneElement(el, {
  style: el.props.style ? {...el.props.style, flex: value} : {flex: value},
});

<div [fxFlex]={33} />

from react-future.

scottmas avatar scottmas commented on May 3, 2024

There's just SOOOO many cool things you can do in so much more conveniently once attribute directives are opened up as an option.

As a straw man proposal, consider if we allowed there to be a single __attr prop holding an object containing the attribute component and its argument, like so:

React.createElement('div', {__attr: {type: SomeClass, argument: 123}}, 'Some text');

To make this easy to use, of course there would be a JSX transform

<div *SomeClass={123}>Some text</div>

When an attribute directive is invoked, the element will automatically be wrapped inside SomeClass.
This is trivially easy to accomplish by monkey patching React.createElement like so:

  const origCreateElement = React.createElement;
  React.createElement = newCreateElement;

  function newCreateElement(...args){

    if(!(args[1] || {}).__attr) return origCreateElement.call(this, ...args);

    const { type, argument } = args[1].__attr;
    args[1] = Object.assign({}, args[1]);
    delete args[1].__attr;

    return createElementWrapper.call(this, type, {argument}, origCreateElement.call(this, ...args));
  }

With this monkey patch in place, an attribute directive implementing an async conditional becomes trivial to implement as you can see in this CodePen here.

Attribute directives gain even more win 🥇 once multiple attribute directives are supported:

<If condition={true}>
  <Bold>
    <Flashing>
      Hello World
    </Flashing>
  </Bold>
</If>

vs

<div *If={true} *Bold *Flashing>Hello World</div>

We can argue all day long about the merits of either approach, but most developers would eventually opt for the second approach if given the choice. I'm reasonably confident it would naturally happen for simple wrapping components of the sort I'm describing.

Aaaanyhow, this was a long post. And take everything for the straw man proposal it is. But this is one possible way attribute directives could be implemented. And it doesn't appear like it would break existing code, besides having to blacklist a magic property __attr or the like.

from react-future.

scottmas avatar scottmas commented on May 3, 2024

@syranide Those are some solid proposals. However, what I don't love is how they destroy the semantic readability of the html. The problem I have is with components that exist solely to modify behavior, not add structure. Components like a theoretical <Flashing/> component exist to modify behavior of existing elements, they don't actually add structure. For example

<div> {// <-- structural component}
   <Widget> {// <-- structural component}
        <Flashing> {// <-- behavioral component!!!}
            <span>Hi people</span> {// <-- structural component}
        </Flashing>        
   </Widget>
</div>

Whereas with attribute directives all the elements/components in the JSX would define structure:

<div> {// <-- structural component}
   <Widget> {// <-- structural component}
       <span *Flashing>Hi people</span>  {// <-- structural component}
   </Widget>
</div>

When all the components on a page are meant solely to provide structure, things become much easier to parse and reason about. The point isn't just to save some key strokes.

@lettertwo Those are some prime examples of structural components that should not be implemented as proposed attribute directives. Attribute directives by convention should exist only to modify behavior of a structural element. I totally agree that your second code example is the correct choice.

@matthewwithanm We all love JSX! Can you describe more what would make you sad? It may be possible to describe some definition of an attribute directive that wouldn't corrupt JSX in your view. Maybe a directive could simply be an observer listening to changes on the component and intercepting the output of the render method. Or something like that, instead of a full blown component. Or maybe the attribute directive could be a class decorator of sorts.

I'm really interested to hear your thoughts. Is there another solution we could find to make React more friendly and inclusive? 75%+ of the dev world have only ever written HTML and attribute directives would make their transition to React much smoother. As long as it doesn't hurt performance or the simplicity of React/JSX? There's a good portion of the dev world who wants their HTML to look like HTML and if we can, I think React should try to appeal to them as well. I think we need to be careful to not fall victim to group mentality where we define ourselves as a community by our differences with other frameworks like Angular, but try to stay as open and as inclusive as possible.

from react-future.

scottmas avatar scottmas commented on May 3, 2024

Those are some great points Lettertwo. I'm totally on board with keeping React functional in nature. Function composition is a beautiful thing and React should stay functional.

Because of this, I totally agree that flow control should never be done via attribute directives

//Should never be possible!!!
<div *reactFor={items as item}>{item.name}</div>

And if attribute directives are simply wrapping components, this is already impossible. Attribute directives are one to one translatable to components. Full stop.

At that point, attribute directives would simply become a syntactic sugar for wrapping components.
Flashing(Thing()) and <Flashing><Thing /></Flashing> and <Thing *Flashing/> would all mean the exact same thing. See my Code Pen for a concrete example of this in practice.

With a syntactic sugar like this, it maybe gets a bit harder to picture visually the nested function wrapping, but we lose no functionality and become more developer friendly. Most importantly, we keep the functional nature of JSX/React.

from react-future.

scottmas avatar scottmas commented on May 3, 2024

Also, with an appropriate JSX transform, we could keep the exact same rendered semantics:

<div *Attr>Hi</div>

gets transformed by JSX to

React.createElement(Attr, null, React.createElement('div', null, 'Hi'))

from react-future.

natew avatar natew commented on May 3, 2024

@scottmas why not write one now to test it out? Would be a good way to test the proposal. You could use $props style to avoid needing to modify language.

from react-future.

scottmas avatar scottmas commented on May 3, 2024

@natwe That's a great idea, although I've never written one before. Time to learn more about babel's guts...

Good points @matthewwithanm. We're all here just trying to talk about how we might improve a framework we all love. It fills me with great sadness every time I am forced to work in Angular 2 and believe me you, I am not an Angular fan boy. In fact this whole issue came about from me trying to convince a developer friend that he should use React, but then there were several "killer" features in Angular that he decided he couldn't do without and that aren't concisely implementable in React w/o wrapping attribute directives. The point is, Angular has some features I admire, among them attribute directives. I was only trying to make the point that it is not a valid argument to say "Angular does it, so therefore it's bad". I don't want to fill anyone with sadness either.

The conceptual simplicity of React is something I think we all love and want to keep. Let's keep it "just javascript"™️ - I'm with you 100%. I only offered the idea of attribute directives being decorators or observers as other potential options if the attribute directive === wrapper component line of reasoning doesn't work out.

As for 75%+ of the dev world only ever having written HTML, I still stand by that. Have those 75%+ also used attribute directives? Definitely not. But have those devs ever added a class to an element to hide it? Almost universally, yes. If so, they already understand how this works:

<div *Hide={someVar}>Hi</div>

Whereas they would look at the following in incomprehension:

{someVar && <div>Hi</div>}

I personally prefer the first, but there's no objective reason one of these is better than the other. Lots of React devs, such as yourself, likely prefer the second. However, it is objectively true that many, many more developers in the world will be able to more rapidly understand the first. Attribute directives are a much simpler conceptual jump to make.

I haven't talked about passing props to attribute directives nor multiple attribute directives, but would a JSX transform of the following really be so incomprehensible and complicated?

<div *Attr1={someVar} *Attr2>Hi</div>

to

<Attr2>
   <Attr1 default={someVar}>
      <div>Hi</div>
   </Attr1>
</Attr2>

to

React.createElement(Attr2, null, 
   React.createElement(Attr1, {default: someVar}, 
       React.createElement('div', null, 'Hi')))

Where attribute directives are applied from left to right and an attribute prop is passed as default. There could be weird corner cases which these simple rules don't define exactly but I doubt there would be very many.

Supporting something like the transformation described above would produce several effects: (1) barely increase the complexity of JSX - it would still be "just javascript"™️, (2) Make development a lot more pleasurable for many devs (myself included), and (3) Make React much more approachable to a sizable portion of humanity.

from react-future.

satya164 avatar satya164 commented on May 3, 2024

I'm talking about this,

<div *Attr1={someVar} *Attr2>Hi</div>

being transpiled to

React.createElement(Attr2, null, 
   React.createElement(Attr1, {default: someVar}, 
       React.createElement('div', null, 'Hi')))

or basically,

<Attr2>
  <Attr1 default={someVar}>
    <div>Hi</div>
  </Attr1>
</Attr2>

It looks nothing like HTML to me.

Anyway, regarding <div *if={true}>Hello</div>, what is it actually transpiled to?

from react-future.

scottmas avatar scottmas commented on May 3, 2024

@lettertwo I think you are definitely right that they wouldn't understand how the first form works. But I think in most cases devs would understand what it would do. And that's a fair point that it could maybe lead them astray in their conceptual model of React. I guess I'm optimistic though that this could be gotten around with proper documentation, etc. Any in many cases, devs don't know or care how framework internals work. They just want to use some predefined tools and build their applications without doing anything custom or fancy.

And you are right that JSX being "just javascript" does get muddied once <Component ... is no longer always equivalent to React.createElement(Component, .... That muddying is a sacrifice I'd be willing to make for greater expressiveness, but it definitely would increase the learning curve of JSX. But I do think it would only increase the learning curve, not the actual complexity of JSX. It would still be relatively transparently transpiled to raw javascript React.createElement calls.

@satya <div *if={true}>Hello</div> would be transpiled to

React.createElement(If, {default: true}, 
   React.createElement('div', null, 'Hi'))

from react-future.

danielmiladinov avatar danielmiladinov commented on May 3, 2024

from react-future.

scottmas avatar scottmas commented on May 3, 2024

Perhaps I could have better phrased it "run time programmatic complexity of React". What I mean is that the running React code wouldn't be forced to do anything it doesn't do already. It would still just be dealing with components.

As an analogy, take another "sugar" construct, like class and method decorators. There's basically nothing that you can do with decorators that you can't do with Object.define, but decorators are arguably easier to write, reason about, and consume. Are decorators destroying the simplicity of Javascript? Maybe a little. But they're also darn convenient. I'm trying to make the same argument for attribute directives.

<div *flashing *highlight={someVar}>Hi</div>

is just easier to write, read, and think about about then its compiled equivalent

<Highlight default={someVar}><Flashing></div>Hi</div></Flashing></Highlight>

And I think the benefits of the above approach become more apparent the less steeped you are in React. Talk to any dev on the street who hasn't programmed in React and ask them which of the two above code snippets they would prefer to write.

I recognize the same argument can be made for any syntactical sugar proposal. So really it's a question of costs vs benefits. How many developers would the sugar make happier + make more inclined to use React vs how many devs would it make mad + make less inclined to use React? I'm inclined to believe there's a lot of devs out there (likely not in this conversation) who would like the addition a whole lot, and a decent amount of existing React devs who would like it too despite the added complexity to JSX.

from react-future.

chrisregnier avatar chrisregnier commented on May 3, 2024

yo dawg, it looks like you've put a component in a component :P
But seriously, it does look like this is just trying to hide wrapped components behind new syntax, so I'd ask why you wouldn't just create another component instead?

const BoldFlashingComponent = (props) => (<Bold><Flashing>{props.children}</Flashing></Bold>);
...
<BoldFlashingComponent><span>Hi</span></BoldFlashingComponent>

or

@Bold @Flashing (<span>Hi</span)

I much prefer the explicit nature over using the attributes for the sake of terse embedded code here <span *bold *flashing>Hi</span> . And I definitely don't think the extra line is problematic (of course I've never been one for trying to make things terse for the sake of saying more with less characters).

Now I'm not exactly saying the special attributes wouldn't be useful for declaratively describing behaviour modifications on a component, in fact I think it seems like a great idea, although I admit I'm coming up blank on types of behaviour changes that I would feel the need to have them embedded within jsx rather than having the logic separate in code or another explicit component.

I would also hate to see conditional logic used this way. Trying to create a component to do conditional logic, like an if, seems like an extremely heavy handed way of doing things. And it's extremely expensive in comparison to the simple branch that it's trying to duplicate ie. {condition && (<div />) }. I love jsx specifically because the logic is completely separate from the component html-like syntax. And cause the component syntax doesn't try to hide anything from me. The only thing I would like see related to conditional logic is a better way to do a single if statement so that I can pull the component syntax outside of my logic blocks { ... } ...more like {{ conditional }} <Component /> (but this is probably better for a different issue).

Are there other use cases you can think of that this syntax would enable? ie things that can't be done currently rather than just ease of writing?

from react-future.

chrisregnier avatar chrisregnier commented on May 3, 2024

Ok I finally came up with some other uses for myself.
For example, react uses the attributes ref and key, but moving them to these special attributes could free up the attributes for the actual component. So you could treat this syntax as a way of setting metadata on a component.

<MySecurityComponent key="cipherkey" *key="react-unique-key" />

Then if the jsx implementing library gave you some hooks to do something with the metadata, you could register some middleware that would be able to interpret the metadata however you want.

So in your case you could add in a styling middleware.
babel plugin: [jsx-attribute-styler]

<span *style={bold} *style={flashing} >Hi</span>

And *style could be interpreted as something that wraps the span with a Bold and Flashing component in a specific order (order may be difficult if it's just a map though, but diff issue), or it could set some css flags, all depending on what the middleware does. And something like the babel react preset would enable the jsx-attribute-react plugin to read key and ref attributes.

The beauty with this is it provides the ability for the generic hooks, but makes it a user buy-in, which then bypasses all of the arguments 'we all' have for and against different syntax styles for enabling logic and other capabilities. Ya the more I think about this, the more I think this could be awesome and provide the community with lots of power to do what they want without alienating react/jsx devs that like their syntax a specific way.

from react-future.

jamesmfriedman avatar jamesmfriedman commented on May 3, 2024

Long thread, lots of great opinions. I'm coming from AngularJS, and I'm learning to live without directives, but there is one use case I really miss.

Form validation was never easier. I could write a ton of little single use validator functions and drop them on an input to change its behavior. For example:

<input type="text" validate-email="multiple" validate-min-length="5" validate-has-chars="@mydomain.com" />

I know I can find ways to express the same logic without attribute directives, but it ends up being extremely ugly, and I feel like I'm adding semantic structure where I really just want to upgrade logic.

My current solution for this particular case is to create a form validator component and pass it list of validations for the child element. This might fit this particular case with minimal bloat, but it does feel like a workaround for what could be a much simpler directive API.

<Validate validators={{email: 'multiple', minLength: 5, hasChars: '@mydomain.com'}}>
    <input type="text" />
</Validate>

from react-future.

jamesmfriedman avatar jamesmfriedman commented on May 3, 2024

Yeah, that would be a bit cleaner, thanks for the tip.

from react-future.

scottmas avatar scottmas commented on May 3, 2024

@jamesmfriedman Yes. This. Form validation is perfect example in my view of something that are perfectly suited to attribute directives.

Using only components, it becomes impractical to compose atomic directives in a practical way. Instead of composing components, devs are forced to use kitchen sink all in one components of the type that you created or which @KyleAMathews created. And if the all-in-one component doesn't have the particular validation you want, they have no recourse other than to submit a PR, use a new library, or try some potentially kludgy work around.

But the compositional approach looks like this:

 <ValidateEmail arg="multiple">
    <ValidateMinLength arg="5">
        <ValidateHasChars="@mydomain.com">
            <input type="text" value={this.state.value}/>
         </ValidateHasChars>
    </ValidateMinLength>
</ValidateEmail>

Which this is monstrous, and I personally would never do it this way. The code becomes too hard to follow.

But with syntactic sugar, it becomes sane once again:

<input type="text" *ValidateEmail="multiple" 
                   *ValidateMinLength="5" 
                   *ValidateHasChars="@mydomain.com" />

The big benefit I'm advocating here is that attribute components allow us to use component composition in a practical way. Which opens up a whole new way of thinking about the components we write.

When components are simple, they can become the infrastructure for more complex components - in the same way that dead simple NPM packages you've never heard of become the building blocks for more complex ones. This building blocks approach simply isn't possible with the monolithic kitchen sink components React devs have traditionally written.

Also, kitchen-sink components begin creating these weird React DSLs of <Input>'s created by four different package authors (case in point: react-input, react-input-component, react-input-field, and react-text-input) instead of letting us stick with the plain DOM primitives and extend their default behavior.

from react-future.

lettertwo avatar lettertwo commented on May 3, 2024

@scottmas, to @KyleAMathews's point, your example glosses over the fact that you could simply implement a component that renders an input and performs validation from props. So, instead of this:

<input type="text" *ValidateEmail="multiple"
                   *ValidateMinLength="5" 
                  *ValidateHasChars="@mydomain.com" />

You could have this:

<EmailInput multiple minLength={5} hasChars="@mydomain.com" />

The cost of directly using the 'primitives' from react-dom to preserve semantics is that you miss out on the power and expressiveness of React's component model almost entirely.

from react-future.

scottmas avatar scottmas commented on May 3, 2024

@syranide, your pseudo code looks like perfectly sane and normal React code in the way we have traditionally done things.

But in what way am I "abusing the concept of components"? Your point about needing to store validator results in state is a good one, and could take some thought on how to address. But specifically, why is nesting lots of components inherently a bad thing? Some slight performance concerns are the only thing I can think of.

I'm interested in your perspective also on the points I raised, that the traditional approach forces us to

  1. Use monolithic kitchen sink components ( - still monolithic even if they expose a plugin interface - which is basically what you're describing with the custom validators like blahValidator)
  2. Force us to use DSL abstractions like <EmailInput/> instead of the primitives exposed by the environment (<input/> in the DOM, <TextInput/> in React Native, etc).
  3. Require lots more logic and code. Your example is perfectly sane traditional React code, but it is quite verbose compared to the proposal.

from react-future.

syranide avatar syranide commented on May 3, 2024

@scottmas

But in what way am I "abusing the concept of components"? Your point about needing to store validator results in state is a good one, and could take some thought on how to address. But specifically, why is nesting lots of components inherently a bad thing? Some slight performance concerns are the only thing I can think of.

It's a complex topic and I haven't thought about it enough to have all the right terminology. But I think it's inherently wrong to render a validator, a validator takes a value and gives you a result, it's a simple function. It has nothing to do with React. A good validator can be used for any UI framework. But you should render the results of the validation, and how that looks should be flexible. In my daily work I focus heavily on achieving certain design goals. Perhaps I want to show all input validations at the same time, perhaps only the most important, perhaps I just want to show the error on the active input, perhaps I want to have a summary at the bottom. Your solution seems to only accommodate a few of these, and only in certain ways.

So don't get me wrong, the way you've done is not necessarily wrong if you have a very specific outcome in-mind and you're fine with the tradeoffs. But does not seem like a good foundation for a language feature, it needs to have large usefulness and should avoid luring developers into corners.

You should leverage JS for complicated logic, not use JSX and components as some kind of meta language. I.e. the same reasons if/conditional-rendering is done via JS and not JSX directives. JS is far more expressive and suited for task.

Use monolithic kitchen sink components ( - still monolithic even if they expose a plugin interface - which is basically what you're describing with the custom validators like blahValidator)

I don't really use components made by other people, we design from scratch for our specific purpose. Partly because of the beliefs of my team, but also because I haven't seen anyone do it right. I truly believe useful reusable component libraries are possible, but the few I've seen have gotten it terribly wrong being trapped in the HTML mindset or are very opinionated and useless to us. But I haven't looked very far.

But I don't see how having a <FooBarInut /> with validator support is "monolithic". It can be a simple input underneath with only the essential code for handling a generic validator interface and what should be done when a validation error occurs. It basically becomes your helper component for achieving a certain functionality, if you want to have different behavior somewhere else, you make a different version of it, but use the same validators, etc.

To put it differently, IMHO the right way to think about these things is; you have a bunch of features and you want to put them together side-by-side, they should all be aware of each other. Having an <input /> which magically become validated because it's nested inside a <MagicValidator /> and which post data to a certain source because it's in a <MagicForm> is really alien to me.

Force us to use DSL abstractions like instead of the primitives exposed by the environment ( in the DOM, in React Native, etc).

That is fine if you have very specific needs for certain components (i.e. perhaps the email input has some visual aids or whatever), other than that if you end up with that result you've done it wrong.

EDIT: I never use the raw <input /> directly, it's always hidden inside one or more (reusable) components.

Require lots more logic and code. Your example is perfectly sane traditional React code, but it is quite verbose compared to the proposal.

Yes it's a lot of code, but the point is that you then stick this code in a simpler reusable component with a very specific purpose, i.e. validate my field and show the result in a certain way. If you're unhappy with that later, want to do it another way, perhaps you have a field which is very special... then you're now free to take that code and make a different component. Flexibility. But you're now making the trade-offs on a per component basis, not on a project-wide basis.

The way I think about this is that I want to have a large set of features than I can package into simple purpose-made reusable components. I do not want to have a bunch of generic feature components which I chain together which can achieve a predefined set of behaviors but can go no further.

So I'm not afraid of writing a little more code, that's how my team operates. We prefer to achieve specific design goals over writing short code, we do not evolve our design goals from predefined components. Flexibility trumps convenience.

Again, I haven't thought this through massively, but I hope some of it makes sense. If you're trying to achieve certain practical goals (flexibility, reusability, etc) then one can discuss code or more concrete aspects around it. But if writing terse code is the goal irrespective of technical demands, then it's in my opinion pointless as it always devolves into each individuals' thresholds for how many shortsighted shortcuts they're willing to make irrespective of the technical trade-offs.

from react-future.

brunoAltinet avatar brunoAltinet commented on May 3, 2024

I just wanted to add one more example to the discussion. I'm currently tasked to put tooltips over different components. The way you do this is as follows:
<ToolTip text="this is a tooltip"> <SomeComponent></SomeComponent> </ToolTip>

As opposed to:
<SomeComponent *toolTip="tooltip"></SomeComponent/>

Admittedly, there's a shorter version of this, and that is to create a function and just do:
toolTip("text tooltip", <SomeComponent></SomeComponent>)

But, it's still pretty pedestrian and don't get me started on when you start composing multiple functions.
I don't know, JSX obviously has its great sides (eg. debugging, simplicity), but i do feel that historically it's absolutely a step backwards in expressiveness compared to previous frameworks i worked with (XAML,Angular etc.). JSX is easy, but not simple.

from react-future.

brunoAltinet avatar brunoAltinet commented on May 3, 2024

Probably the wrong analogy. If you look at it from the point of toolsets, where you need get something done, the simple tools and concepts that React provides you don't scale that nicely with data intensive apps; They're easy to pick up, but hard to master in a way that doesn't leave you perpetually uncomfortable and writing boilerplate. The way i have to think when using react and the redux to handle complex cases does leave me scratching my head and feeling like i'm dealing with unfit tool for the job a lot (the tooltip is just one example, redux-form library, although the best forms library, suffers a lot from the same type of issues, where boilerplate and boilerplate wrapping is your only way out sometimes). I have a junior and i really can't explain some of the code and data flow to him when it comes to react, it just gets MESSY.
Angular directives are more complex, but they adress the need, and address it well in a pretty DRY way. You do need some extra effort upfront but it pays dividends when you start getting it. You simply need that. You need a way to sprinkle behaviour on elements in a sane way, because that's what a huge majority of modern UI libraries I used have in one way or the other. I do believe that when doing that simple/easy judgement, it heavily depends on your use case too. So yeah, maybe Angular is not simple or easy, but there's nothing simple or easy about our job either.

from react-future.

pigoz avatar pigoz commented on May 3, 2024

For different reasons than the ones you mention I agree that redux-form's API is not very good anymore if you consider how much the react community progressed in the past years. That is not React's fault in any way though (and there are better new libraries).

I have wrapped redux-form in a functional API with great developer ergonomics (using fp-ts and io-ts, two libraries that I can really recommend), since I have so many redux-form forms in my project, that it doesn't make sense to move away from it.

I am not really sure I'd want to add validation information directly in the JSX. As your application's scope increases, it's generally a good idea to move that stuff away from the view code anyway. As mentioned in previous comments though, if you want to, you can do it in React as well by creating a Field component that takes validations as props.

from react-future.

brunoAltinet avatar brunoAltinet commented on May 3, 2024

Not so much interested in validation use case, as in having the simple ability to add a tool tip to a component without hijacking the component itself, which happens alot because there's a whole class of problems that are taken care of be HOC which conceptually turn the whole thing on it's head, now your damn helper is a container of your component instead of other way around!

There's also a whole set of tools like redux-orm and immutable which try to control the data flow in a controllable way. But then, you made it easy, but it aint simple anymore! Trying to debug those sorts of hairballs gets old fast.

What i'm trying to say, React is like Paint.NET to me, and Angular closer to Photoshop. Sure, i can't do jack in photoshop and it would prolly take a month course to grasp it and start doing stuff, but React needs a crapload of plugins and helpers which get you eventually to the same point, only it sucks. Like you just said, redux-form is not hot anymore and now i feel like a made a wrong call, but guess what, angular forms is still a good decision!

On a constructive note, i'd be interested to have a look at your favourite alternatives to redux-form.
Cheers.

from react-future.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.