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

Clarify v15.0.0 changelog note about JS component klasses #6496

Closed
wants to merge 1 commit into from

Conversation

jmm
Copy link

@jmm jmm commented Apr 12, 2016

There's an item in the v15.0.0 release notes (changelog and blog post) that's super confusing, because I think it's a mistake.

Functional components can now return null.
(@jimfb in #5884)

This part...

React 0.14 still allowed you to define a class component without extending React.Component or using React.createClass() [...] This issue is solved in React 15

...implies that you can no longer do this:

var Klass = function () {
  return {
    render () { return whatever; }
  };
};

render(<Klass />);

And apparently that was the case mistakenly and temporarily from #5884 but fixed in #6008 without being reflected in the changelog.

My understanding is that the actual change is that you can no longer do class Hello {} vs class Hello extends React.Component {} (because it wouldn't have Hello.prototype.isReactComponent). (Aside: I don't actually fully understand why it works like that, since it seems like you could duck type on Hello.prototype.render to differentiate from stateless functional, but I haven't looked at all of it and there must be a reason.)

I've attempted to clarify that in the changelog. (If this is correct it'd also be good to update the blog post if the content is going to be duplicated there.)

The change is that you can no longer do `class Hello {}` vs
`class Hello extends React.Component`; you can still do
`function Klass () { return {render} }`.
@jmm jmm changed the title Clarify v15.0.0 changelog note about JS klass components Clarify v15.0.0 changelog note about JS component klasses Apr 12, 2016
@jimfb
Copy link
Contributor

jimfb commented Apr 12, 2016

@jmm class component refers to things that are not stateless function components (so I think the change log is technically correct as-is), but should probably make that more clear. Your new wording is slightly less precise given the React vocabulary we use, so we're still going to need to massage it a bit.

How about this as an alternate wording:

Functional components can now return null. We added support for defining stateless components as functions in React 0.14. However, React 0.14 still allowed you to define an ES2015 class component without extending React.Component, so we couldn’t reliably tell if your component is a function or a class, and did not allow returning null from a stateless functional component. This issue is solved in React 15, and you can now return null from any component, whether it is a class or a function. (@jimfb in #5884)

My changes bolded for visibility, real copy should retain standard formatting.

Adds "ES2015" qualifier to class component, and removes or using React.createClass(), for clarity. Resolves ambiguous "it" to a stateless functional component. Would that have resolved your confusion?

@jmm
Copy link
Author

jmm commented Apr 12, 2016

@jimfb Thanks for the feedback.

Your new wording is slightly less precise given the React vocabulary we use, so we're still going to need to massage it a bit.

Hmm, sorry about that, I was trying to make it more precise. I have to admit that I find the React vocabulary pretty woolly. Referring to stateless functional components in particular is very awkward. It's also confusing that so many terms are used somewhat interchangeably: component, component class, React class, class component, ES6 class component, probably more. I'm not sure class component is a good choice in any context. ES2015 component class, though a bit awkward, would probably be better.

I'd appreciate if you could tell me where I went wrong with the React vocabulary.

Consider these examples:

we couldn’t reliably tell if your component is a function or a class
whether it is a class or a function

To me this language is confusing because:

  • They're all functions.
  • class as a general term has fuzzy meaning in JS and React and some of this passage seems to be talking about ES2015 classes while some seems to be talking about ReactClasses.
  • It blurs the distinction between component and component class.
function SFC (props) { return element; } // <= function
React.createClass(opts) // <= function, class, ReactClass
function () { return {render}; } // <= function, ReactClass
class Child extends React.Component {
} // <= function, class, ReactClass, ES2015 class

class component refers to ES6 classes (so I think the change log is technically correct as-is)

How about this as an alternate wording:

What would this mean?:

allowed you to define a ES2015 class component without [...] using React.createClass()

Is that describing a scenario like this?:

var Child = class extends React.createClass({render}) {
  /* ... */
}

@jimfb
Copy link
Contributor

jimfb commented Apr 12, 2016

It's also confusing that so many terms are used somewhat interchangeably: component, component class, React class, class component, ES6 class component, probably more. I'm not sure class component is a good choice in any context. ES2015 component class, though a bit awkward, would probably be better. I'd appreciate if you could tell me where I went wrong with the React vocabulary.

They're not entirely interchangeable. It's like the difference between a square and a rectangle. Technically, all squares are rectangles, but not all rectangles are squares. Similarly, a class component is a component but not all components are class components. There are several nuances like this that we should do a better job of documenting.

  • component = A thing you would render (some frameworks use the term "widget", if that's helpful). An example might be a NewsFeedStoryWidget or a DatePickerWidget. It's a user-interface concept.
  • component class = Short for a "react component class". A definition of a widget. This is a programming language concept; it is the implementation of the widget.
  • react class = Short for a "react component class". Same thing as a "component class" (in the context of React). We should probably just pick one term, but we'd need to bike shed on which one. We could always use the long version, but if you always use the long version of all terms, your sentences become very long and incomprehensible. The full term for "stateless function" is "stateless react component definition function" or some such permutation of those words.
  • class component = A component that will, when instantiated, have a backing instance. Explicitly not a "stateless function component".
  • ES6 class component = A component that is explicitly defined using ES6 classes.

Part of the problem/ambiguity is inherent in the english language. For instance, you mention that a class is a function (which is technically true, and an implementation detail of the design of classes), but when we refer to a function (as oppose to a class) we generally don't intend to also capture classes in that statement (but sometimes we make general statements about functions which also apply to classes because classes are functions). Similarly, when someone says they "have a class", they could be referring to the source code, or to a reference to a JSVM entity representing that class, etc. When they say they "have a component", they could be referring to the source code, or to a class object, or to an instance of that component class, etc. English sucks.

ES2015 and React.createClass are mutually exclusive, so the statement would make no sense. Removed in my new revision:

Functional components can now return null. We added support for defining stateless components as functions in React 0.14. However, React 0.14 still allowed you to define an ES2015 class component without extending React.Component, so we couldn’t reliably tell if your component is a function or a class, and did not allow returning null from a stateless functional component. This issue is solved in React 15, and you can now return null from any component, whether it is a class or a function. (@jimfb in #5884)

Anything still confusing about this draft?

@jmm
Copy link
Author

jmm commented Apr 14, 2016

They're not entirely interchangeable.

Yes, that's why it's important to have terminology that clearly communicates what concept it's referring to. For example, this:

Similarly, a class component is a component but not all components are class components

A component instance (ReactComponent) isn't the same as a component definition (ReactClass or stateless functional component definition) isn't the same as the high level concept of component. I was already under the impression that component class means what you mean by class component and thought that was just a variation that's trying to express that it's also an ES2015 class.

I think all of this can be expressed in English, but it's certainly easy to lose clarity when writing about stuff like this, especially without well defined terms.

Right now there are too many terms that are too vague, inconsistent use of terms to refer to the same things, and too many things referred to imprecisely by the same terms.

There are several nuances like this that we should do a better job of documenting.

👍 👍

component class
react class [...] We should probably just pick one term

That would be helpful. This illustrates why it's difficult to precisely use React vocabulary. As much as possible I try to use the terminology from React (Virtual) DOM Terminology since it defines specific terms — that aren't easily mistaken for high level concepts or references to the general meaning of a word — for concrete interfaces. That has a definition for ReactClass that applies to the kinds of component definitions I labeled with it in my snippet earlier (everything but stateless functional). But your definition of react class also encompasses stateless functional component definitions, while your definition of class component is what seems to align with ReactClass. Even those docs aren't consistent with themselves, using the term ReactComponent Class in places instead of ReactClass, and they don't even mention stateless functional components.

We could always use the long version, but if you always use the long version of all terms, your sentences become very long and incomprehensible. The full term for "stateless function" is "stateless react component definition function" or some such permutation of those words.

This is an example of what I mean that the current terminology makes these very awkward to refer to. "stateless function" for example doesn't even make sense, but I've been tempted to say it too for lack of a better option.

ES6 class component = A component that is explicitly defined using ES6 classes.

class component

I think its going to be very confusing to have both component class and class component in the vocabulary and meaning different things. "Class" is such an overloaded term it might be helpful to try to move away from that to something less ambigious like "definition" except when referring to ES2015 classes.

ES2015 and React.createClass are mutually exclusive, so the statement would make no sense.

Ok, thanks. I got the impression that #5884 actually removed the ability to create a plain ES5 JS component "class" and it seemed like that's what the changelog was describing by referencing both of those.

Anything still confusing about this draft?

Short of having a better vocabulary to describe this stuff I think just omitting some of the vaguer usage of function and class would be clearer:

Functional components can now return null. We added support for defining stateless components as functions in React 0.14. However, React 0.14 still allowed you to define an ES2015 class component without extending React.Component, so we couldn’t reliably tell if your component is a function or a class,what kind your component is and did not allow returning null from a stateless functional component. This issue is solved in React 15, and you can now return null from any component, whether it is a class or a function.

It's up to you though. When I read the changelog and created the PR it seemed to me like a straightforward issue: that behavior changes from #5884 were logged and then undone in #6008 without being reflected in the log, but maybe I misunderstood or it's more complicated.

@jimfb
Copy link
Contributor

jimfb commented Apr 14, 2016

,what kind your component is and did not allow

This sentence structure still feels awkward to me. How about:

, so we couldn’t reliably determine the component's type, and thus couldn't allow returning null from a stateless functional component.

So the paragraph becomes:

Functional components can now return null. We added support for defining stateless components as functions in React 0.14. However, React 0.14 still allowed you to define an ES2015 class component without extending React.Component, so we couldn’t reliably determine the component's type, and thus couldn't allow returning null from a stateless functional component. This issue is solved in React v15, and you can now return null from any component.

@@ -15,7 +15,7 @@
- **`data-reactid` is no longer on every node.** As a result of using `document.createElement`, we can prime the node cache as we create DOM nodes, allowing us to skip a potential lookup (which used the `data-reactid` attribute). Root nodes will have a `data-reactroot` attribute and server generated markup will still contain `data-reactid`. ([@spicyj](https://github.com/spicyj) in [#5205](https://github.com/facebook/react/pull/5205))
- **No more extra `<span>`s.** ReactDOM will now render plain text nodes interspersed with comment nodes that are used for demarcation. This gives us the same ability to update individual pieces of text, without creating extra nested nodes. If you were targeting these `<span>`s in your CSS, you will need to adjust accordingly. You can always render them explicitly in your components. ([@mwiencek](https://github.com/mwiencek) in [#5753](https://github.com/facebook/react/pull/5753))
- **Rendering `null` now uses comment nodes.** Previously `null` would render to `<noscript>` elements. We now use comment nodes. This may cause issues if making use of `:nth-child` CSS selectors. While we consider this rendering behavior an implementation detail of React, it's worth noting the potential problem. ([@spicyj](https://github.com/spicyj) in [#5451](https://github.com/facebook/react/pull/5451))
- **Functional components can now return `null`.** We added support for [defining stateless components as functions](/react/blog/2015/09/10/react-v0.14-rc1.html#stateless-function-components) in React 0.14. However, React 0.14 still allowed you to define a class component without extending `React.Component` or using `React.createClass()`, so [we couldn’t reliably tell if your component is a function or a class](https://github.com/facebook/react/issues/5355), and did not allow returning `null` from it. This issue is solved in React 15, and you can now return `null` from any component, whether it is a class or a function. ([@jimfb](https://github.com/jimfb) in [#5884](https://github.com/facebook/react/pull/5884))
- **Functional components can now return `null`.** We added support for [defining stateless components as functions](/react/blog/2015/09/10/react-v0.14-rc1.html#stateless-function-components) in React 0.14. However, React 0.14 still allowed you to define a component `class` without extending `React.Component` (e.g. `class Hello {}`), so [we couldn’t reliably differentiate between a stateless functional component and a `ReactClass`](https://github.com/facebook/react/issues/5355) and did not allow returning `null` from a stateless functional component. This issue is solved in React 15, and you can now return `null` when rendering any component, whether it is a `ReactClass` or stateless functional. ([@jimfb](https://github.com/jimfb) in [#5884](https://github.com/facebook/react/pull/5884))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about flipping this to be using pure functions as "stateless" components? I think it gets the idea across a little more clearly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume you're referring to changing "defining stateless components as functions".

I have no objections. To be clear, class components can also be "stateless" and therefore technically match the term "stateless components", but I don't think that makes your version incorrect.

@jimfb
Copy link
Contributor

jimfb commented Apr 15, 2016

So the paragraph becomes:

Functional components can now return null. We added support for using pure functions as "stateless" components in React 0.14. However, React 0.14 still allowed you to define an ES2015 class component without extending React.Component, so we couldn’t reliably determine the component's type, and thus couldn't allow returning null from a stateless functional component. This issue is solved in React v15, and you can now return null from any component.

@jmm
Copy link
Author

jmm commented Apr 20, 2016

@jimfb The change you suggested to sentence structure seems fine to me.

I'm kind of on the fence about whether pure function is a good way to describe these. Is it technically true, for example with respect to stuff like autoFocus and emission of warnings? I also feel like it muddles the meaning a bit that the behavior is affected by properties on the function like defaultProps and contextTypes that can be mutated between invocations, even though I guess it's not technically incorrect since those translate into different arguments being passed.

In any case, while I hope it's clear that the React terminology would benefit from an overhaul, I don't think introducing new terminology for these in the changelog is going to help clear things up.

@gaearon
Copy link
Collaborator

gaearon commented Jun 26, 2016

I’m going to close this—to be honest I don’t think it helps a lot (e.g. ReactClass is not a term we commonly use in the docs even though it might be on the “terminology” page). Water under the bridge now 😉 . Thanks for taking the time though!

@gaearon gaearon closed this Jun 26, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants