React first steps
Tags: react, tutorial, javascript, beginners
Published: 2019-09-22 14:44:11

They started using react at work, so I set up myself for the bare minimum tutorial-based experiments (watch your step! I am learning *while* I type the post).

You can use [jsbin](http://jsbin.com) or [repl-it](https://repl.it/) for this, but I already had [yarn](https://yarnpkg.com/lang/en/) installed so I copied the config from repl.it example:

# Config (yarn):
```js
{
  "name": "runner",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.9.0",
    "react-dom": "^16.9.0",
    "react-scripts": "2.1.5"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ]
}
```
With this you can install dependencies with the `yarn install` command.

# Minimal app:

**Html:**
I only added `<div id="app"></div>` to a basic and empty HTML5 file because React needs an element to render into.

Saved on `public/index.html` per yarn convention.

```html
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>React 101</title>
</head>
<body>
  <div id="app"></div>
</body>
</html>
```

**Javascript:**

Saved on `src/index.js` per yarn convention.

```js
import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
  <h1>hello world</h1>,
  document.getElementById('app')
)
```
Build this with `yarn build` 

This is needed because I am going to use [JSX](https://jsx.github.io/) to write React equivalent of templates. JSX is a language that translates to normal html but allows variable interpolation and some logic embedding.

> You can use [React _without_ JSX](https://reactjs.org/docs/react-without-jsx.html) (using `createElement` or `e` helpers), or use it without a build step (by compiling with a babel script), but using it is the most popular option, and mostly trivial if you are going to have a build step anyway. I do not _recommend_ it (I don't have enough experience on React to have an informed opinion), just explain why I am using it for this post.

Test it on your browser with `yarn start`

It will default to serve locally on `localhost:3000`, but so does *Ruby on Rails* so if you are using both on your machine, do not try to run them at the same time, or change the configuration on any of the two.

**Output:**
![Helo world output](https://thepracticaldev.s3.amazonaws.com/i/647eyxn31fdoytdtnhgp.png)

# Components and props

Let's add what React calls a _component_, i.e. a separate part of the interface with its own markup, logic and state.  

```js
// imports omitted from now on for brevity

function Hello(props) {
  return <h1>Hello, {props.name}</h1>;
}

ReactDOM.render(
  <Hello name="Oinak" />,
  document.getElementById('app')
);
```
**Output:**

![Hello me](https://thepracticaldev.s3.amazonaws.com/i/rs2ao86u9psphujoscse.png)

A lot happened here:

A function receiving `props` and returning JSX is a minimal component.
`function f(props){ return <span>any jsx</span> }`

Curly braces allow interpolation inside JSX;
`Hello {props.name}` becomes "Hello Oinak"

A tag on capitals is replaced by a component of the same name, and its attributes become props:
`<Hello name="Oinak" />` calls `Hello({ name: 'Oinak'})` and is replaced by its output: `<h1> Hello, Oinak</h1>`.

Function components are a shorthand for full fledge ES6-style classes:
```js
// function Hello(props) { return <h1>Hello, {props.name}</h1>;}
class Hello extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
```
They mean the same, but the function is shorter if you don't have to do anything with the constructor, state, etc...

So let's build an app that actually does something, I am going to go crazy original here and build a To-Do list because it's something no one ever in the history of the internet used to learn a js framework.

So first, I take the code from before and create a component for the input:

```js
class Input extends React.Component {
  render() {
    return (
      <div className="Input">
        <input type="text" />
        <input type="button" value="+" />
      </div>
    );
  }
}

ReactDOM.render(
  <div>
    <h1>TO-DO</h1>
    <Input />
  </div>,
  document.getElementById('app')
);
```
Now the `Input` component has a text box and a button with a plus sign on it.

The idea is that you write your list item text on the box and click on the '+' button when you are done.

This code is good enough for the input GUI:

![To-Do item input](https://thepracticaldev.s3.amazonaws.com/i/si0eas8p7w4u3dow1w0e.png)

But it does nothing.

I need two more things, the code to store new items and to display them. Let's start with the latter:

I chose to represent the list as an html ordered list, so each item is simply a list item `<li>Like this</li>`. With that idea, `Item` component can be like this.

```js
class Item  extends React.Component {
  render(){
    return <li>{this.props.text}</li>
  }
}
```
This code assumes that you call it like this: `<Item text="Hello">` so that a `text` attribute gets saved into `props` by the default React constructor.

Now, I change the main call to `ReactDOM.render` to use the Item component:

```js
ReactDOM.render(
  <div>
    <h1>TO-DO</h1>
    <Input />
    <ol>
      <Item text="Hello" />
      <Item text="World" />
    </ol>
  </div>,
  document.getElementById('app')
);
``` 

Then you get this:
![Full GUI](https://thepracticaldev.s3.amazonaws.com/i/wz3ovpd3st8rmb979j86.png)

We have a _mockup_!

For the next steps we need some new concepts:

## Event handling

**State**:
We set initial state in the constructor via `this.state = ...` but when components render depends on their state, we need to _tell__ React that we need a new render, thats what the `setState` method is for, it updates the state and triggers a new render. There are two versions:
```js
this.setState({ key: value });
```
and, if current state depends on previous state or props:
```
this.setState(function(state,props){
  return {
    // something based on previous state or props
  };
})
```
We need also function binding, to keep event handlers' `this` bound to the component.


```js
class Item  extends React.Component {
  constructor(props){
    super(props);
    this.state = { done: false };
    this.toggleDone = this.toggleDone.bind(this); // bind this
  }

  toggleDone() {
    // this is the component because of the binding
    this.setState({done: !this.state.done, render: true});
  }

  render() {
    // change style depending on state:
    const elementStyle = (this.state.done ? {textDecoration: 'line-through'} : {});
    return (
      <li style={elementStyle}>
       <input type='checkbox' value={this.state.done} onClick={this.toggleDone} />
       <span> {this.props.text} </span>
      </li>
    );
  }
}
```
With this, we are able to change the state of `Item` components, and React will automatically change their rendering.

**Before click:**

![List with checkboxes](https://thepracticaldev.s3.amazonaws.com/i/c5es2unjph9eefmfocjl.png)

**After click:**

![Strikethrough element](https://thepracticaldev.s3.amazonaws.com/i/n4id51gjuitil9n8iov2.png)

Inline styles won't make your design pals happy, but we will get to that later.

## Handling events outside component

Now we have a problem, the interface for adding elements is in the `Input` component, but the **state** affected by this event has to be outside because if affects all the _App_ and will be rendered by `Item`'s.

This is our new `Input`:
```js
class Input extends React.Component {
  constructor(props) {
    super(props);
    this.state = {text: ''};                  // initially empty
    this.onChange = this.onChange.bind(this); // store input text on state
    this.addItem = this.addItem.bind(this);   // handle '+' button
  }

  addItem() {
    this.props.onAddItem(this.state.text); // call external handler
    this.setState({text: ''});             // empty the field
  }

  onChange(e){ this.setState({text: e.target.value}); }

  render() {
    return (
      <div className="Input">
        <input type="text" onChange={this.onChange} value={this.state.text}/>
        <input type="button" value="+" onClick={this.addItem}/>
      </div>
    );
  }
}
```
There are two events being handled here:

### Input

The text input `change` calls onChange, similar to the `toggleDone` from the previous section, but in this case I store the current text from the input on the component's state attribute: `text`.

### Add item

When you click the plus button, we read current text from the state and call `this.props.onAddItem`, and that `props` means that this is an event handler passed from outside. After that, we clear the text field to get ready for a new item.

We cannot test this just yet because we need corresponding changes outside:

## The Todo Component
We need a place to put App state, and the event handler that listens to Input, but acts somewhere else:

```js
class Todo extends React.Component{
  constructor(props){
    super(props);
    // initial state to verify rendering even before adding items
    this.state = { items: ["Example", "other"] };

    // bind the event listener, just like before
    this.addItem = this.addItem.bind(this);
  }

  addItem(value){
    // add the new item to the items list
    this.setState( { items: this.state.items.concat(value) } );
  }

  render(){
    // there is no `for` on JSX, this is how you do lists:
    const listItems = this.state.items.map((i,n) =>
      <Item key={n.toString()} text={i} />
    );

    return (
      <div>
        <h1>TO-DO</h1>
        <Input onAddItem={this.addItem}/>
        <ol>
          {listItems}
        </ol>
      </div>
    );
  }
}
```
Pay attention to the `<Input onAddItem={this.addItem}/>` part on `Todo`'s render. It is what connects `Todo`'s `addItem` with `Input`'s `onAddItem`.
I used different names on purpose so that it's slightly less confusing.

When you click the '+' button on `Input` it reads its own `state.text` and calls `Todo`'s `addItem` which sees that text as `value`, and adds it to `this.state.items` list. By doing it with `setState` we tell React that `Todo` needs a new render.

The new render calculates `listItems` based on `this.state.items` and renders an `Item` component for each one of them.

To use it you need to change the call to `ReactDOM.render` to this:

```js
ReactDOM.render(
  <Todo />,
  document.getElementById('app')
);
```

**Before click:**
![Ready to add item](https://thepracticaldev.s3.amazonaws.com/i/v4a59q2e87qsdhuz2qub.png)

**After click:**
![Added item](https://thepracticaldev.s3.amazonaws.com/i/p8yg08kb6batds8vo7op.png)

## Extra credit

Now we can add items and check them, so we are mostly done, but I want to go a bit further, so I am going to add a couple of improvements:

### Remove elements:
```js
class Item  extends React.Component {
  constructor(props){
    super(props);
    this.state = { done: false, render: true };   // store render flag
    this.toggleDone = this.toggleDone.bind(this);
    this.destroy = this.destroy.bind(this);       // new event handler
  }

  toggleDone() {
    this.setState({done: !this.state.done, render: true});
  }
  
  destroy(){ // set render flag to false
    this.setState({done: this.state.done, render: false});
  }

  render() {
    // returning null removes the element from DOM (but not memory!)
    if (this.state.render === false) { return null; }
    const elementStyle = (this.state.done ? {textDecoration: 'line-through'} : {});
    return (
      <li style={elementStyle}>
       <input type='checkbox' value={this.state.done} onClick={this.toggleDone} />
       <span> {this.props.text} </span>
       <input type="button" onClick={this.destroy} className='remove' value='x'/>
      </li>
    );
  }
}
```
I added a new button type input to the items, and linked its click event to the destroy handler. This function just sets a new `render` state attribute to false, but our new render strategy returns _null_ if that attribute is false. When a component returns null from the render function, React removes it from the DOM.

It is not removed from memory, if you examine Todo's state with your developer tools, it is still there. This could be bad in terms of performance, but good for the implementation of an "undo remove" feature. You be the judge.

![Removed element](https://thepracticaldev.s3.amazonaws.com/i/0fd4r8j4ic1bc5emfcf7.png)

### Styles

Up until now you have been looking at no more that raw html elements. However, React allows for the application of per-component styles. The way to do this is create a src/Foo.css file, and add `import './Foo.css';` to your App or Component file.

If you want to know how to get to this, I leave the files below:
![Final render](https://thepracticaldev.s3.amazonaws.com/i/knknye5l1196r6gaxq76.png)

**`src/index.js`**
```js
//jshint esnext:true

import React from 'react';
import ReactDOM from 'react-dom';
import './Input.css';
import './Item.css';

class Input extends React.Component {
  constructor(props) {
    super(props);
    this.state = {text: ''}
    this.onChange = this.onChange.bind(this);
    this.addItem = this.addItem.bind(this);
  }

  addItem() {
    this.props.onAddItem(this.state.text);
    this.setState({text: ''});
  }

  onChange(e){
    this.setState({text: e.target.value});
  }

  render() {
    return (
      <div className="Input">
        <input type="text" onChange={this.onChange} value={this.state.text}/>
        <input type="button" value="+" onClick={this.addItem}/>
      </div>
    );
  }
}

class Item  extends React.Component {
  constructor(props){
    super(props);
    this.state = { done: false, render: true };
    this.toggleDone = this.toggleDone.bind(this);
    this.destroy = this.destroy.bind(this);
  }

  toggleDone() {
    this.setState({done: !this.state.done, render: true});
  }
  
  destroy(){
    this.setState({done: this.state.done, render: false});
  }

  render() {
    // returning null removes the element from DOM (but not memory!)
    if (this.state.render === false) { return null; }
    const elementStyle = (this.state.done ? {textDecoration: 'line-through'} : {});
    return (
      <li style={elementStyle}>
       <input type='checkbox' value={this.state.done} onClick={this.toggleDone} />
       <span> {this.props.text} </span>
       <input type="button" onClick={this.destroy} className='remove' value='x'/>
      </li>
    );
  }
}

class Todo extends React.Component{
  constructor(props){
    super(props);
    this.state = { items: ["Example", "other"] };
    this.addItem = this.addItem.bind(this);
  }

  addItem(value){
    this.setState( { items: this.state.items.concat(value) } );
  }

  render(){
    console.log(`render items: ${this.state.items}`)
    const listItems = this.state.items.map((i,n) => <Item key={n.toString()} text={i} />)
    return (
      <div>
        <h1>TO-DO</h1>
        <Input onAddItem={this.addItem}/>
        <ol>
          {listItems}
        </ol>
      </div>
    );
  }
}

ReactDOM.render(
  <Todo />,
  document.getElementById('app')
);
```

**`src/Input.css`**
```css
.Input input[type=text]{
  width: 25em;
}
.Input input[type=button]{
  background-color: green;
  color: white;
  font-weight: bold;
  border: none;
  font-size: 18px;
  vertical-align: top;
}
```

**`src/Item.css`**
```css
li {
 width: 20em;
 height: 1.4em;
 box-shadow: 1px 1px 2px rgba(0,0,0,0.5);
 margin: 2px 0px;
}

li > input[type=button].remove {
  float: right;
  background-color: firebrick;
  color: white;
  border: none;
  padding: 2px 6px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
}

li.done {
  text-decoration: line-through;
  color: grey;
}

li.pending {
  color: blue;
}
```

## Disclaimer

- This is my first ever React app, it is most probably wrong
- React recommends one js and one css file per component, I did not follow the convention for brevity
- You can use more ES6 features or none at all, it's not imposed by the framework.

What do you think?

Was it useful to you?

Do you have any tips for me to improve?