What, formally speaking, is a react component? - reactjs

Consider
var MyComponent = React.createClass({
render: function() {
//...
}
});
as well as
var element = React.createElement(MyComponent);
Which specifically, or where specifically, is the "component"? Is it the object returned by the call to React.createClass(..)? Is it the object returned by React.createElement(..)? Is it behind the scenes somewhere in between?

There's a post on the React blog that answers your question.
http://facebook.github.io/react/blog/2015/12/18/react-components-elements-and-instances.html
Basically, components are your class definitions, elements are created from your components in render(), and instances are your component's representation in the DOM.

Obviously http://facebook.github.io/react/ is the authority here - a component is conceptual construct that has properties, state, and a render function. Although the first two are optional. Components are typed using a "class" (ES5 prototypes, ES6 classes, etc), which React is formally made aware of using the createClass function. Component instances are elements in a larger whole, loaded into a UI context (the browser, a phone, whatever), usually called an app, and created with createElement.

This post answers the question in great detail.
In short: components are -- literally speaking -- functions that take an object with a props field and, in turn, return a React.Element. Components can be pure functions, or JS classes (which are functions with syntactic sugar over them anyway). You can define a component via React.createClass; the output of React.createClass is -- as I understand it -- still literally a component (although several instances of it can be instantiated behind the scenes via React, each of which can be accesssed via this within the class definition; these instances are also components).
Finally, React.Element is nothing but a "description" of a component instance or a dom node. Literally, it is a javascript object with fields like props and children (it also has a field type, which is described in the article). Crucially, an instance of a React.Element is NOT a component; it merely describes one. Moreover, the output of a React.createClass() is a component, as well as any function that takes an object with a prop field and returns an instance of a React.Element.

Related

Properties on a react returned jsx

I see three (3) properties on a value my react component is returning namely keys, props and type and i am blank on how they work. I even see a fourth one called ref when i use the createElement method to create my DOM elements. Any help that would enlighten me on the area will be much appreciated. Thank you.
Just a note - your questions are asking about the fundamental way that React works (pertaining to the use of the Virtual DOM and its component-based structure). I'll try to sum it up as much as possible and give links to resources that might be helpful.
The key attribute relates to the way React uses the Virtual DOM and one of the reasons it works as fast as it does. In short, React supports the key attribute to improve its ability to differentiate sibling elements/components from each other and prevent re-painting the whole element tree if only one of the sibling changes. I suggest reading further on this to better understand it.
The props attribute relates to how React passes data from one component to another (usually from a parent component to a child component). Think of them as the arguments that you pass to a function - where the function is React's internal way of compiling your code and eventually rendering the DOM elements.
type should be pretty straightforward - its used to determine the type of component/element (eg: div, or a React function component)
The ref attribute relates to a way that you can store references to a particular element. By passing a React ref to a component/element's ref attribute, you're essentially storing access to that component/element through the React ref. There are some pretty good explanations out there about how it works on a conceptual level.
Note that my use of the word "component" refers to a React components, and "element" refers to the DOM element

Are props.render and props.children equal?

I can't find a distinction between props.render and props.children, so I'm starting to believe that they achieve the same thing BUT in different ways.
Is this a correct assumption? If not; how are they different (beside how they are implemented), and when should I use one over the other?
render-props can address your questions.
children is part of the Top-level API, meaning, it is intrinsic to a react component. The children prop can be just about anything react can render, typically an element or array of elements, but can be a function similar to a "render prop".
The "render" prop, however, is not intrinsic, it can be anything really... so long as it is a function.
Using props other then render
It’s important to remember that just because the pattern is called
“render props” you don’t have to use a prop named render to use this
pattern. In fact, any prop that is a function that a component uses to
know what to render is technically a “render prop”.

How are React component instances different than other frameworks?

In the docs, React says that it doesn't really care for instances as the Components take props as input and outputs elements for you. It gives you an example at the top of how other frameworks have to create an instance and then connect it to the DOM to handle different events. But I don't understand how this is different than what React is doing.
You're not calling new on your component in React, but you still have to render it and create all the same handlers. And this inside the component still refers to the instance, so doesn't React still have to create an instance each time your component is rendered (even if it's a component inside an <li> that's being rendered several times at once).
Traditional frameworks will have to create multiple instances of the same component to connect to each DOM node it corresponds to, isn't that what React is doing too? How else can one component keep track of multiple this's?
Indeed, React creates Component instance internally. You don't need to worry about using new.
React Element is just a plain JavaScript Object that describes what you want to be rendered (React.Component or HTML Element, if type is a String).
From the docs:
An element is not an actual instance. Rather, it is a way to tell
React what you want to see on the screen. You can’t call any methods
on the element. It’s just an immutable description object with two
fields: type: (string | ReactClass) and props: Object1.
The difference is that you the developer are not having to write the code to do all that. You just write the render method and your callbacks and let React worry about creating the DOM elements and the component instances and connecting them together.

Explanation for wrapping React HOC components with scalajs-react

I am trying to understand #trepidacious's scalajs-react wrapper for this HOC react component.
1a) Why is the type of the wrapped component here ReactComponentC[P,_,_,_] ?
1b) Why is the return type of the component ReactComponentU_ ?
def wrap[P](wrappedComponent: ReactComponentC[P,_,_,_]): Props => P => ReactComponentU_ = {
2) Why is the factory function passed to SortableElement here ?
val componentFactoryFunction = js.Dynamic.global.SortableElement(wrappedComponent.factory) ?
Does not SortableElement take a Component ?
3) Why are the wrapped props passed like this ?
"v" -> wrappedProps.asInstanceOf[js.Any]
What is the reasoning behind this line ?
Where is that magical v is coming from ? Is it from scalajs-react or from react-sortable-hoc ?
4) What is the reasoning behind this wrapper ? If I want to write a wrapper for another HOC component, what should be the general recepie for that ?
There are quite a lot of parts here, but I've put together some links working from the lowest level to the highest while covering the questions.
The first and most important definitions are of React Components and React Elements. This page has an in-depth explanation - I recommend completely skipping the "Managing the Instances" section since it muddies the waters by describing a traditional UI model while using terms differently to the way they are used in React. In summary my understanding is that:
A React Component is a general concept that can be implemented in multiple ways. However it is implemented, it is essentially a function from props (and optionally state) to page contents - a renderer.
A React Element is a description of page contents, representing a particular rendering of a Component.
React components and props docs describe the two ways of defining a React component, the first one is a function from props to a react element, this is the one we're interested in. The React.createFactory docs then confirm that we can pass such a function to createFactory. As far as I can tell this exists in order to adapt from the multiple ways of defining a React Component (by subclassing React.Component or React.PureComponent, by using React.createClass, or by a function from Props to ReactElement) to a way of rendering props to a ReactElement. We can see something about this by looking at this gist introducing React.createFactory in React 0.12 - essentially they wanted to introduce some abstraction between the class used to define a React Component and the eventual function from props to React Elements that is used when rendering, rather than just letting the class render props directly.
Next we have a minor wrinkle - React.createFactory is flagged as legacy in the docs. Luckily this isn't a major issue, again as far as I can tell React.createFactory(type) just produces a function f(props) that is identical to React.createElement(type, props) - we are just fixing the type argument in React.createElement. I've tested this in the react-sortable-hoc wrapper, and we can use createElement instead of createFactory:
val componentFunction = js.Dynamic.global.SortableContainer(wrappedComponent.factory)
(props) => (wrappedProps) => {
val p = props.toJS
p.updateDynamic("v")(wrappedProps.asInstanceOf[js.Any])
React.asInstanceOf[js.Dynamic].createElement(componentFunction, p).asInstanceOf[ReactComponentU_]
}
We're now nearly at question 2). If we look at the source for SortableElement we can see that the sortableElement function accepts a WrappedComponent argument - this is used to create another React Component, via the "subclass React.Component" approach. In the render function of this class, we can see that WrappedComponent is used as a React Component, so we know that it is indeed a component, even without a static type :) This means that WrappedComponent needs to be something accepted by React.createElement, since this is what a JSX component use desugars to.
Therefore we know that we need to pass to the sortableElement function something that is usable as a React Component in javascript React.createElement function. Looking at the scalajs-react types doc we can see that ReactComponentC looks like a good bet - it constructs components, presumably from props. Looking at the source for this we can see that we have two promising looking values - reactClass and factory. At this point I realise that the code is probably using the wrong one - I've tried replacing .factory with .reactClass and this still works, but makes much more sense since we have the comment to tell us that it gives Output of [[React.createClass()]], which is one of the options for a valid React Component. I suspect that factory also works by essentially wrapping up the provided component in createFactory twice, since the output of createFactory is also usable as its input... I think given this correction we've answered question 2 :) This also pretty much answers question 1a) - ReactComponentC is the scala trait that gets us the .reactClass val we need for a scala-defined React Component. We only care about the type of props it uses (since we have to provide them), hence the P. Since scala IS typed we know that this is what we get from building a scala React Component in the normal way (at least for components I've tried).
On question 1b), I found the type ReactComponentU_ from code like the ReactCssTransitionGroup facade in scalajs-react Addons and the scalajs-react-components notes on interop, which shows wrapping of a non-HOC component. Looking at the type itself we can see that it extends ReactElement, which makes sense - this is the expected result of rendering a React Component. In our wrap function in the SortableElement and SortableContainer facades we are producing (eventually) another function from props to ReactElement, just one that jumps through a few hoops to get there with the HOC approach. I'm not sure why ReactComponentU_ is used instead of just ReactElement, I think this is to do with tracking the state of components through the type, but the code still compiles if I return ReactElement, which is odd.
Question 3) is much easier - scalajs-react works with Props that can be Ints, Longs etc. but in Javascript these are not objects. To make every scalajs component's props be an object, scalajs-react wraps them in an object like {"v": props}, and then unwraps again when they are used. When we wrap a Scala React Component using the HOC, we need to get that wrapped component's props to it somehow. The component produced by the HOC function (the "wrapping" component, SortableElement or SortableContainer) does this by expecting its own props to already contain the wrapped component's props as fields, and it then just lets these flow through to the wrapped component, for example in SortableElement's render:
<WrappedComponent
ref={ref}
{...omit(this.props, 'collection', 'disabled', 'index')}
/>
this.props is passed through to the wrapped component. Since the wrapped scala component requires a "v" field with the scala props object in it, we need to add this to the wrapper component's props. Luckily this field will then pass through unaltered to be interpreted later by the scala component. You won't see the "v" field since scalajs-react will unwrap it for you.
This does raise a problem when wrapping some other HOCs - for example ReactGridLayout's WidthProvider measures the wrapped component's width and passes it through in the props as {"width": width}, but unfortunately we can't see this from scala. There may well be some workaround for this.
That covers the details and references for the parts of the HOC wrapping, but actually the process is pretty easy (providing you don't want to access props "injected" into the wrapped component):
Make a scala object for the facade to organise the code.
Work out what props are required by the wrapper component. For example in SortableElement this is index, collection and disabled. Make a Props case class with these fields, in the facade object.
Write a 'wrap' function in the facade object. This does the following:
Accepts a wrappedComponent: ReactComponentC[P,_,_,_] and passes it to the javascript HOC function (e.g. SortableElement) to produce a new React Component.
Builds a javascript props object with the wrapper component's props AND the magic "v" field with the wrapped component's props.
Uses javascript React.createElement function to produce a ReactElement that renders the wrapped component, and casts this to ReactComponentU_.
Note that at stage 5 we need to convert from our Scala Props case class (the wrapper component's props) to a plain javascript object that can be understood by the HOC. The wrapped component's props just go straight into the "v" field without conversion, just casting to js.Any.
The code I wrote for SortableElement and SortableContainer splits this up a little so that wrap returns a curried function that accepts the props for the wrapper component and produces another function from the wrapped props to the final React element. This means that you can provide the wrapper props once and then use the resulting function like a normal component in your Scala render code.
I've updated the SortableElement facade with the improvements above, and this is pretty much a minimal example of a HOC facade now. I would imagine other HOCs will look very similar. You could probably abstract some of the code for the purposes of DRY, but actually there's not really a lot there anyway.
Thanks for the questions and for helping work this out - looking back through the process and particularly your question on .factory has left me more confident that this is now working the right way (with the changes described).
I don't have all the answers but my understanding is that the author of scalajs-react uses lots of types to prevent errors during construction of components as well as during the lifecycle of the components once constructed. He uses naming conventions with suffixes and letters to separate the types which make sense but can be daunting at first.
1a) ReactComponentC is a constructor for a component (C as in Constructor).
1b) ReactComponentU_ represents an unmounted native (JavaScript) React component.
3) I think, looking at the scalajs-react source, that yes, "v" is a magic key name. There is (was?) also some notes in the source code to the effect that this is not ideal ;)
There is a plan to simplify scalajs-react's types in a new version.

Should "component" functions in Om be called directly?

As I'm only starting to fully understand, om.core/build and om.next's factory functions return React element objects, which refer to component functions/classes, and the actual component is only instantiated later by React's reconciler. That is, (om.core/build some-component data) doesn't actually call some-component immediately.
However, we often represent simple, "stateless" components as just functions which take props and return a React element. In the (pure) React world, you'd use one of these functions like a component class, as React.createElement(AStatelessComponent, {some: "props"}), or more conveniently in JSX as <AStatelessComponent some="props" />. Those too return a React element which references AStatelessComponent, which won't actually be called until later.
But in Om, when we have a simple component like this (and by "we" I mean me and my team, at least), we call the function directly. Thus,
(render [this]
(om/div {}
(a-stateless-component {:some "data"})))
Here, a-stateless-component is called immediately, and whatever it returns is inserted directly into the div, rather than being substituted later by the React reconciler.
Is there a preferred way to React.createElement in Om? Or is it preferred to just call functions like this directly, even though it skips creating a component instance in the render tree?
In Om, if you want to instantiate a stateless component you need to call js/React.createElement directly.
Why you would want to do that depends:
if you call React.createElement you get a "tracked" instance in React's reconciler
if you don't, you get inlining but the stateless component now cannot be distinguished from its parent in React's render tree.
EDIT: I just realized that om.next/factory is permissive enough that it allows you to instantiate the stateless components you were talking about. So you can achieve what you want both by calling js/React.createElement directly on a function of props, or by calling om.next/factory with that same function as argument.
Here's a working example. The following code:
((om/factory #(dom/div nil "Hello, World")))
results in the following component (in React's devtools):

Resources