React

React is a library for building User Interfaces. You can think of a React application as a function which receives data as input and produces your UI as output. Things like data fetching, advanced state management, routing, etc. are outside of the scope of React itself.

Components and Elements

React abstracts away DOM manipulation. You won't create DOM nodes and attach and remove event handlers. Instead, you'll write your own Components - UI structures with lifecycle methods and data handling - and compose these Components together to form a nested tree of Elements - pure JS objects representing the desired form of your App. React will take this representation of your app and perform the necessary DOM manipulation for you.


// defining a component...
class ContrivedExample extends React.Component {
    render() {
        const person = this.props.person;

        return (
            React.createElement("p", {}, `Hi ${person}, I'm a contrived example!`)
        );
    }
}

// and creating a representation of how we want our App to look like
const contrivedExample = 
    React.createElement(ContrivedExample, { person: "Visitor" }, [])

// and injecting our "app" into the page
ReactDOM.render(contrivedExample, document.body)

This will render a p tag with a greeting into the document:

Hi Visitor, I'm a contrived example!

We have seen a few things in the example above:

  1. We define our custom UI pieces as Components with a render function, which returns React Elements
  2. React Elements are used to represent simple DOM tags as well as our custom, more complex Components
  3. We never instantiate DOM elements directly - we only create React Elements whose type  (the first argument to React.createElement) is a string of the DOM element we want
  4. In order to instruct React to use our custom components, we simply call React.createElement with a reference to our custom Component.
  5. We can pass data to our Components using props.
  6. Our job as UI developer consists of writing the components we need and assembling them together as a tree of React Elements.
  7. The actual rendering and DOM manipulation of the app is done by ReactDOM.

This concludes our first look at React. Next we'll have a look at React's infamous JSX templating library.

JSX

Before React came onto the scene a good five years ago, it was considered best practice to keep a clear separation between HTML, CSS, and JavaScript. A page should use semantic HTML which renders fine without CSS, be enriched with CSS to communicate the brand and aid in readability, and finally, JavaScript *could* be added to add interactivity to a page. But each part of the whole should be kept separate, and work independently from each other.

Actually, let me rephrase that. That is still considered best practice. There are exceptions to this rule, however. Whenever a page includes client-side data fetching or provides means for interaction which go beyond following links, a clear separation between HTML and JS is not possible anymore.

The JS logic which adds interactivity to a website needs to be aware of the structure of the markup it operates on. Otherwise, the JS code can't target the relevant pieces of a page. The markup and the code are tightly coupled by nature, and if one changes, the other has to change as well.

The authors of React embraced this need for co-location of markup and code, and enabled it via the introduction of JSX - React's templating language, or in their own words, "syntax extension". It looks like HTML embedded in JavaScript code:


class ContrivedExample extends React.Component {
    render() {
        const person = this.props.person;

        return (
            // notice the embedded <p> tag...
            <p>Hi {person}, I'm a contrived example!</p>
        );
    }
}

// ...and how we treat our custom component just like an extension of HTML,
// passing in data as we would define attributes on a tag
const contrivedExample = <ContrivedExample person="Visitor" />

ReactDOM.render(contrivedExample, document.body)
                    

These tags are actually not tags at all. They are instructions to a JSX-aware compiler to transform them into calls to React.createElement. That's all. We are not suddenly writing HTML in our JavaScript. Every single line in the example above is still JS code, executed by a browser's JS interpreter after being loaded on the page.

The way it works:

  • If the tag is lowercase, React creates an Element whose type is a string representing a native HTML tag. If it is uppercase, React creates an Element whose type is a reference to a developer-defined custom Component.
    
    <p></p> // React.createElement("p")
    
    class P extends React.Component { /* */ }
    <P></P> // React.createElement(P)
                                
  • The content between the tags is passed to the React Element as its children property.
    
    <p>
        This example will be compiled to one React Element of type "p".
        It will have a children property, and its value will be a string.
    </p>
    
    // will compile to...
    {
        'type': 'p',
        'props': {
          'children': 'This example will...'
        }
    }
    
    <CustomComponent>
        <p>
            This example will be compiled to three React Elements. 
            One of type CustomComponent.
        </p>
        <p>
            The CustomComponent Element will have a children property 
            with an array of two React Elements of type "p".
        </p>
    </CustomComponent>
    
    // will compile to...
    {
        'type': CustomComponent,
        'props': {
          'children': [
            {
              'type': 'p',
              'props': {
                'children': 'This example will...'
              }
            },
            {
              'type': 'p',
              'props': {
                'children': 'The CustomComponent...'
              }
            }
          ]
        },
    }
                                
    (In the two examples above, when I say "will be compiled to an Element...", I actually mean "will be compiled to a call to React.createElement which will create an Element...")
  • Attribute assignments are passed to the React Element as its props.

Props and Children

There are two ways to pass data into a Component: props and children. Props (short for properties) are plain JavaScript values of whatever nature (primitives, objects, functions) which you can pass to a component using the attribute notation in JSX. The name of the attribute will be the key on the props object under which the respective value can be found.


class Header extends React.Component {
    render() {
        const message = this.props.message;

        <h1>My message is {message}!</h1>

    }
}

<Header message="very important" />
                            

Props are a generic way to pass whatever JS construct you need to a component. Another way to configure a Component is using Children. This is used to pass additional things to render to a component. This additional content is available inside of your component under props.children, and you can render it by simply referencing it as an expression in your component's render method.


class MyComponent extends React.Component {
    render() {
        <p>
            <h1>This is my component</h1>
            // ...
            {this.props.children}
        </p>
    }
}

<MyComponent>
    <p>It renders its children via this.props.children</p>
</MyComponent>
                            

During my time with React, I've seen a gross overuse of props to configure the desired behaviour of components, when using children would have been much cleaner and simpler. Let's assume you have a sidebar with some navigation links. If the user is logged in, an additional Log Out  link should be shown, and a Log In otherwise.

An approach to solve this could be to add a boolean isLoggedIn via props, and add a condition in the render method to decide which link to add to the list. Now the sidebar becomes aware of user state. Well, that's what we wanted, right? But what if we want to reuse the sidebar in other contexts, where we do not want this behaviour? Then it would make sense to add another prop, shouldRenderLogInLogoutLinks, right? First we check if the additional link should be rendered, and then we check which of the two we should render.


<Sidebar shouldRenderLogInLogoutLinks={true} isLoggedIn={false} />

// ...

class Sidebar extends React.Component {
    render() {
        const { shouldRenderLogInLogoutLinks, isLoggedIn } = this.props;

        return (
            // ...
            shouldRenderLogInLogoutLinks && isLoggedIn && <LogOut />
            shouldRenderLogInLogoutLinks && !isLoggedIn && <LogIn />
            // ...
        );
    }
}
                            

This desire to make thinks reusable via props can lead to a very serious and unmaintainable prop creep. A much cleaner solution is to make the sidebar render any additional children it receives at the end of the navigation list. Depending on the context where we use the sidebar, we either pass in a Log In link, or a Log Out link, or nothing at all.


<Sidebar>
    <LogIn />
</Sidebar>

// ...

class Sidebar extends React.Component {
    render() {
        return (
            // ...
            {this.props.children}
        );
    }
}
                            

← Back