This is a blog of AmberBit - a Elixir and Ruby web development company. Hire us for your project!

Polish Up your JavaScript #02: Adding React

Hubert

Posted by Hubert Łępicki

Hubert is partner at AmberBit. Rails, Elixir and functional programming are his areas of expertise.
@hubertlepicki @hubertlepicki

In series so far:

  1. Setting up development project with Webpack
  2. Adding React

In the last episode…

In the previous post we have set up working development environment with Babel 7 and Webpack 4. Look there if you haven’t set it up just yet, and download our example repository for code examples.

Random awesome Polish thing #02: Białowieża Forest (photo credit: Freeimages.com/michalski)

Install your NPM packages

You will need react and react-dom packages, plus an extra preset for Babel 7, to make it understand React templating (JSX).

$ npm install --save react react-dom
$ npm install --save-dev @babel/preset-react

Edit your .babelrc to instruct Babel to use the above preset when transpiling your src/index.js and any other source files:

{
  "presets": ["@babel/preset-env", "@babel/preset-react"]
}

We need additional preset, because React uses special syntax, which looks like HTML tags embedded into JavaScript. In order to expand these to appropriate function calls, @babel/preset-react is needed.

React components

Open up your src/index.js file, and change it slightly so we append an empty div element to our web site’s body. This will be the place, where we mount the top-level React component:

...

const reactRoot = document.createElement("div");
reactRoot.setAttribute("id", "react-root");
document.body.appendChild(reactRoot);

This should add the empty <div /> tag to the HTML dynamically. Check it out in development tool by right-clicking on the body of the page and clicking “Inspect”.

We also need to import React and ReactDOM at the very top of the file:

import "./index.scss"
import ReactDOM from "react-dom";
import React from "react";

...

And finally, we need to write our component and render it.

const App = function(props) {
  return(<div>Hello, React!</div>);
}

ReactDOM.render(<App />, reactRoot);

Our component is called App and you make it render within our reactRoot by executing ReactDOM.render function.

The <App /> tag will be transpiled to simple function call. In our example we could also get away with:

const App = function(props) {
  return(<div>Hello, React!</div>);
}

ReactDOM.render(App(), reactRoot);

and the result would be precisely the same, but the first syntax is preferred.

Arrow functions

As we can see, our React component is a simple function, that returns some HTML elements. These will be inserted into our container (reactRoot) when we execute this function.

Since we are writing ES6+ JavaScript, we will have a shorter function syntax on hand. It’s called Arrow functions, and there are slight differences between using function() and arrow function syntax - but we can ignore them for now as they are irrelevant for our example. We will explore arrow functions in details in next part.

The arrow function syntax for our component would be:

const App = (props) => <div>Hello, React!</div>;
ReactDOM.render(<App />, reactRoot);

Class components

Most of components you write in React these days are in the form of functions. Different kind of components, however, exists: class-based components.

Class components are currently still needed on occassion for most React applications, but my strong prediction is they will become thing of the past very soon.

You should write function components as a default way of writing your code.

For the sake of experimenting, let’s try turning our function component into class-based:

class App extends React.Component {
  render() {
    return <div>Hello, class-based React!</div>;
  }
}

ReactDOM.render(<App />, reactRoot);

Component composition

While writing your application as single component is perfectly possible, no one really wants to do that. React’s strength shows when you compose your components. It’s in the name: “component” is all about composition!

Let’s write a simple React component which lists tasks. We’ll start from top to bottom, and the components we’ll create will be as follows:

  1. App - our top-level component. It will hold the list of tasks in it’s state,
  2. Tasks - will render the list of tasks,
  3. Task - will render individual task item.

Starting from the top, we need to update our App component, to have a constructor which sets internal state to initial value having list of tasks in it:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      tasks: [
        {id: 1, completed: false, description: "Write blog post"},
        {id: 2, completed: false, description: "Publish blog post"},
        {id: 3, completed: false, description: "Profit!"}
      ]
    }
  }
  ...
}

And now we want to display these tasks in our render() function. If we wanted to get stuck with single component, we could simply do:

...

render() {
  return <ul>
    { this.state.tasks.map( (props) => <li key={props.task.id}>{props.task.description}</li> ) }
  </ul>
}

This will instruct the React component to return the list of tasks. Notice how we have to specify the special key property for our list items. This is needed in order for React to perform quick re-renders of the list, for example when additional tasks were added. When we specify key, React will know during re-rendering that it’s dealing with either existing or new task, and will not attempt to change all DOM elements of the list, instead will efficiently add the new <li> element to the list without touching the others.

Right, but how can I make the above better with composition?

As mentioned already, we want to extract the <ul> and <li> elements to own components: <Tasks /> and <Task /> accordingly:

class App extends React.Component {
  ...
  render() {
    return <Tasks tasks={this.state.tasks} />;
  }
}

const Tasks = (props) =>
  <ul>
    { props.tasks.map((task) => <Task key={task.id} task={task} />) }
  </ul>

const Task = (props) =>
  <li>
    { props.task.description }
  </li>

We did create a top-level App component, which returns Tasks component passing it list of tasks as a property. Then, we are doing the same between Tasks and individual list item Task. This property passing technique is the basic way of passing down data and state from parent to child components.

The other interesting thing in the example above is that we are mixing class components with function components. Why is that?

Class components can hold state in special property this.state. We can then update this state (using special function this.setState(newState)), which will trigger re-rendering of the component. This will come in handy when we add functionality to complete tasks or add new tasks to the list.

Function components can also hold state, with a new mechanism called “Hooks”. We will not discuss Hooks just yet, and use class-based components and shortly MobX to hold state for us in a structured manner.

In the next step we will see how we can update the state by adding and completing tasks, but also discuss JavaScript functions, their binding and scope.

Further reading

Check out React documentation for component and props: Components and Props

Sources for this blog post

You can find the project’s source files for this blog post on our GitHub under https://github.com/amberbit/polish-up-your-javascript/tree/master/polish-up-your-javascript-1

What’s next?

In blog post #03 we will explore JavaScript functions and stateful React components. Stay tuned!

Hubert

Hi there!

I hope you enjoyed the blog post. Can we help you with Elixir or Ruby work? We are looking for new opportunities at the very moment, and we do have team available just for you.

Email me at: contact@amberbit.com or use the contact form below.

Want to get in touch about a project? Drop us a line!

When submitting the form, you are sending your personal information (including your name and e-mail as entered above) to contact@amberbit.com. AmberBit Sp. z o. o. is the receiving party, and a data controller, and will use the information you provided for the purpose of establishing relationship leading to possibly signing a services contract, and fulfillment of such contract only. We will not subscribe you to marketing lists, newsletters etc. You can read more about it in our Privacy Policy.