2014-12-23

There might be a few things you'll want to know. I wrote about 5000 words worth of thoughts on React before I was able to distill it to the following post, so: if you're used to other frameworks or plain JS and HTML templating engines, and you feel like React is doing something very weird, don't worry: if you treat it as HTML+JS, React is really, really weird. But the reason for it is actually you: React is not HTML+JS, it's plain old object oriented programing... The objects just happen to be able to render themselves as UI elements. The logic behind the programming, as such, depends on knowing that you're doing OOP, and making sure you're thinking about modelling your interactions based on object interactions. React has almost nothing to do with HTML. And for a web framekwork, that's weird. Although only a little.

The best way I can think of to get to business is with a table that compares the various aspects of programming to how you express those things when you're using HTML, versus how you express those things when you're using React's object model. I'm calling it JSX because that's what React refers to it, but really it's "React's object model".

So, here goes:

concepts

HTML

JSX

"thing" to think in terms of

DOM elements

components (React XML elements)

internal structure

DOM tree

components tree

scope

global (window)

local component and this.refs only

contextual id

id attribute

ref attribute

hashcode

n/a

key attribute

mutable properties

HTML attributes

internal object properties

bootstrap values

n/a

component props

property types

strings

any legal JS construct

internal state

...

component state

style assignment

class attribute

className attribute

hierarchy accessors

DOM child/parent access

local access only

content manipulation

DOM API

setState

I'm going to be running through each of these points in order (mostly), and I'd strongly advise you not to skip to just the step "you care about". Unless you gave up on the notion that you're writing a webpage rather than an object oriented application that happens to render its UI elements as inaccessible HTML, you're going to want to read all of them. In fact, humour me: even if you have, read every point anyway. They build on each other.

The "thing" that you're working on

In a traditional web setting, you have the data, the markup around that data, the styling of that markup, and the interaction logic that lets you bridge the gap between the data and the user. These things are typically your data, your HTML, your CSS and your JavaScript, respectively.

That is not how you model in React. React is far more like a traditional programming language, with objects that represent your functional blocks. Each object has an initial construction configuration, a running state, and because we're working on the web, each object has a .render() function that will produce a snapshot of the object, serialized into HTML that is "done" as far as React is concerned. Nothing you can do to it will be meaningful for the object that generated it. React could, essentially, be anything. If it rendered to native UI or to GTK or Java Swing, you'd never know, since React's written in a way that everything your components might conceivably want to do is contained in your object's code.

It's also much more like a traditional OOP environment in that objects don't "take things from other objects". Where in HTML+JS you might do a document.querySelector("#maincontainer div.secondary ol li.current") and then manipulate what comes rolling out of that, in React, you delegate. There is no "global" context to speak of, so you'd have a Main component, containing a Secondary component, and that's all Main can see. If it wants to initiate things on "the current selected list item", it tells the contained Secondary element to take of it, without caring how it takes care of it. The code for doing things lives inside the things that do the doing.

This is pitfall number one if you're starting with React: you're not writing HTML, you're doing object oriented programming with a to-HTML-render pipeline step at the end.

The internal structure of "things"

In HTML+JS, the structure of your functional blocks are just "more HTML". In React, you're using a completely different thing. It's a little bit different in terms of what it looks like (you should be entirely forgiven for thinking you're working with HTML except using XML syntax) but it's completely different in what it is: React markup defines an XML structure that is used as internal canonical representation of your object for UI purposes.

Let's unpack that a little more: the JSX syntax uses tags that map to HTML elements during the final render pipeline step. They are, however, most emphatically not real HTML elements, and certainly not elements that end up being used in the browser. The reason here is that React uses an internal snapshot representation that lets it perform diffs between successive render calls. Each call generates a snapshot, a new snapshot is structurally compared to the previous one, and differences between them are translated into transforms that the browser can selectively apply to the snapshot's associated active DOM. It's super fast, but also means that the JSX you write has almost nothing to do with what the browser will end up using as DOM.

This is pitfall number two, and really is just a rephrasing of pitfall number one: React objects aren't "backed by HTML", nor is the browser DOM "backed by a React object". What the browser shows you is just snapshots of React objects throughout their life cycle.

HTML is unscoped, React objects are scoped as per OOP rules.

Since we have no DOM, and we're doing proper OOP, React components know about their own state, and only their own state. To get around the completely isolation, the normal OOP approaches apply for making components aware of things outside of their scope: they can be passed outside references during creation (i.e. these are constructor arguments; the constructor syntax just happens to look like, but conceptually is not, markup), or they can be passed in later by calling functions on the object that let you pass data in, and get data back as function return or via callbacks.

Which brings us to pitfall number three: If you're using window or some other dsignated master global context in React objects, you're probably doing something wrong.

Everything you do in React you can achieve without the need for a global context. A component should only need to care about the things it was given when it was born, and the immediately visible content it has as described in its render() function. Which brings us to the next section

How do we find the right nodes?

In HTML the answer is simple: querySelect("#all.the[things]"). In React the answer is equally simple, as long as we obey OOP rules: We don't know anything about "higher up" elements and we only know about our immediate children. That last bit is important: we should not care about our children's children in the slightest. Children are black boxes and if we need something done to our children's children, we needSomethingDone(), so we call our children's API functions and trust that they do the right thing, without caring in the slightest how they get them done.

That's not to say you might not want to "highlight the input text field when the main dialog gains focus" but that's the HTML way of thinking. In React, when the dialog gains focus you want to tell all children that focus was gained, and then they can do whatever that means to them. If one of those children houses the input, then it should know to update its state to one where the input renders as highlighted. React's diffing updates will then take care of making sure it's still the same DOM element that gets highlight despite the render() call outputting a new snapshot, with new elements.

So, how do we get our own children? In HTML we can use the globally unique id attribute, and the same concept but then scoped locally to React objects is the ref attribute:
...
will let us manipulate the React XML element that represents as this.refs.abcd in our code. Again: there is no HTML. Similarly, we can select any other React XML element to work with through this.refs so if we have a React element

then we can grab it from this.refs.md and call its functions so that it does what we need, such as this.refs.md.setTitle("Enhanced modal titles are a go");.

The secret sauce in React's diffing: elements have hashcodes, and sometimes that's on you.

Because React generates new XML tree snapshots every time render() gets called, it needs a way to order elements reliably, for which it uses hashcodes. These are unique identifiers local to the component they're used in that let React check two successive snapshots for elements with the same code, so that it can determine whether they moved around, whether any attributes changed, etc. etc.

For statically defined JSX, React can add these keys automatically, but it can't do that for dynamic content. When you're creating renderable content dynamically (and the only place you'll do this is in render(); if you do it anywhere else, you're doing React wrong) you need to make sure you add key attributes to your JSX. And example:

If we don't add key attributes in this JSX, React will see successive snapshots in which every element is different, and will basically effect a full DOM reload in the browser. This is needlessly expensive: with the key attributes, each obj in our list mylist is transformed into an XML element that will eventually end up being rendered as an HTML list item in the browser, while letting React see actual differences in successive snapshots. If, for instance, mylist[1].text changes, its position in the list stays the same, and React will see that nothing structurally changed, only the text content of the first item in the output has changed. As such, it won't even bother telling the browser to refresh the corresponding list item, it'll know to merely effect a .textContent update.

An interesting consequence is that if you use key attributes but use values that collide, React will actually treat different elements with the same key as "the same element", and the last element in a list of same-key elements will be the only one it sees. This, for instance, is a list with only a single item, no matter how many items are actually in mylist:

Mutable properties

We can be brief here: in HTML, all element attributes are mutable properties. In React, the only true mutable properties you have are the normal JavaScript object property kind of properties.

Immutable properties

This one's stranger, if you're used to HTML, because there aren't really any immutable properties when it comes to HTML elements.

In React, however, there are two kinds of immutable properties: the construction properties and the object's internal state. The first is used to "bootstrap" a component, and you can think of it as the constructor arguments you pass into a creation call, or the config or options object that you pass along during construction. These values are "set once, never touch". Your component uses them to set its initial state, and then after that the properties are kind of done. Except to reference the initial state (like during a reset call), they don't get used again. Instead, you constantly update the object's "state". This is the collection that defines everything that makes your object uniquely that object in time. An example

and

We see the child element, with an intial "I have no idea what's going on" state defined in the getInitialState function. Once the element's been properly created and is ready to be rendered (at which point componentDidMount is called) it copies its creation properties into its state, setting up its "true" form, and after that the role of this.props is mostly over. Rendering relies on tapping into this.state to get the to-actually-show values, and we can modify the element's state through its lifecycle by using the setState() function:

The chain of events here is: render() -> the user clicks on the name -> rename() is called, which prompts for a new name -> the element's state is updated so that name is this new name -> render() is called because the element's state was updated -> React compared the new render output to the old output and sees a string diff in the
XML element -> React causes the browser to update the mapped HTML element so we see the new name.

What can I pass as property values?

In HTML the answer is "strings" - it doesn't matter what you pass in, if you use it as HTML attribute content, it becomes a string.

In React, the answer is "everything". If you pass in a number, it stays a number. If you pass in a string, it stays a string. But, more importantly, if you pass in an object or function reference, it stays an object or function reference and that's how owning elements can set up meaningful deep bindings with child or even descendant elements.

If there is no HTML, how do I style my stuff??

React elements can use the className property so that you can use regular CSS styling on them. Anything passed into the className property gets transformed to the HTML class attribute at the final step of rendering.

What if I need ids?

You don't. Even if you have a single element that will always be the only single instance of that React object, ever, you could still be wrong. id attributes cannot be guaranteed in an environment where every component only knows about itself, and its immediate children. There is no global scope, so setting a global scope identifier makes absolutely no sense.

But what if I need to tie functionality to what-I-need-an-id-on element?

Add it:

surprise: you can querySelect your way to this element just fine with a .top-level-element query selector.

But that's slower than ids!

Not in modern browsers, no. Let's move on.

How do I access elements hierarchically?

As should be obvious by now, we don't have querySelector available, but we do have a full OOP environment at our fingertips, so the answer is delegate. If you need something done to a descendant, tell your child between you and that descendant to "make it happen" and rely on their API function to do the right thing. How things are done are controlled by the things that do the actual doing, but that doesn't mean they can't offer an API that can be reached: X:Y.doIt() -> Y.doIt() { this.refs.Z.doIt(); } -> Z.doIt() { this.refs.actualthing.doIt(); }

Similarly, if you need parents to do things, then you need to make sure those parents construct the element with a knowledge of what to call:

and

What else is there?

This kind of covers everything in a way that hopefully makes you realise that React is a proper object oriented programming approach to page and app modelling, and that it has almost nothing to do with HTML. Its ultimate goal of course is to generate HTML that the browser knows how to show and work with, but that view and React's operations are extremely loosely coupled, and mostly works as a destructive rendering of the object: the DOM that you see rendered in the browser and the React objects that lead to that DOM are not tied together except with extremely opaque React hooks that you have no business with.

More posts will follow because this is hardly an exhausted topic, but in the mean time: treating React as a proper OOP environment actually makes it a delight to work with. Treating it as an HTML framework is going to leave you fighting it for control nonstop.

So remember:

you’re not writing HTML, you’re doing object oriented programming with a to-HTML-render pipeline step at the end.

React objects aren’t “backed by HTML”, nor is the browser DOM “backed by a React object”. What the browser shows you is just snapshots of React objects throughout their life cycle.

Your objects have creation properties, but you should always render off of the "current state". If you need to copy those properties into your state right after creation, do. Then forget they're even there.

The best way I can think of to get to business is with a table that compares the various aspects of programming to how you express those things when you're using HTML, versus how you express those things when you're using React's object model. I'm calling it JSX because that's what React refers to it, but really it's "React's object model".

Show more