a sleepy programmer's blog

notes and stuff.

Basic Concepts in React

Since last couple of days, I have been experimenting with ReactJS. I followed the official Tutorial, Thinking in React and some other blogs. This post is a summary of what I learned. I will try to keep is as simple as possible.

What is React? React is a JavaScript library to manage and render Views(V in MVC). The views are rendered by React Components.

Lets read some simple code. A simple ‘Hello React’ program.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello React!</title>
    <script src="build/react.js"></script>
    <script src="build/react-dom.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
  </head>
  <body>
    <div id="example"></div>
    <script type="text/babel">
      var HelloWorld = React.createClass({
        getInitialState: function(){
          return {message: 'Hello'}
        },

        render: function(){
          return (
            <h3>{this.state.message} {this.props.name}</h3>
          );
        }
      });

      ReactDOM.render(
        <HelloWorld name="React" />,
        document.getElementById('container')
      );
    </script>
  </body>
</html>

In the above code sample, we have a HTML page with some JS code defined inside the script tag. The JS code has some HTML too. This HTML inside JS is called JSX. Babel is a library to parse JSX. React uses JSX to render the HTML.

Some basic terminologies:

Component: A component is an entity that is supposed to do only one thing. Ideally, it should follow Single Responsibility Principle. The main task of a component is to render data(props/state). Component doesn’t inherit other Component but data(state/props) is passed form one Component(parent) to another Component(child).

state: A Component’s data that it has not inherited from the parent Component is called state.

props: Data that a Component inherits from the parent/Ancestor Component is called props(properties).

React follows unidirectional data flow. What it means is that a Component(parent) passes data(state/props) to another Component(child). When the data of the parent is altered, the child component automatically update itself.

In the javascript of the above code sample, we have 2 entities named ReactDOM and HelloWorld. In React, these are called Component. In the above code, the ReactDOM is calling the HelloWorld component passing some data(ie. name=“React”). Hence, the parent component is ReactDOM and the child component is HelloWorld. message is the state of HelloWorld whereas name is its props.

Some important points:

  • Component must implement the render function.
  • When state is passed down to child components, it becomes props for child components. ie. state of parent Component will always be props of child Component.
  • A component can alter(update/delete) it’s state since it belongs to the component. state is mutable.
  • A component should not alter(update/delete) it’s props since they don’t belong to the component. props is immutable.
  • If props are to be altered, then they should be altered in the component where they are declared. When mounting the child Component, the callbacks that need to be called to update/delete the props should be passed too.
  • React will update all the child components using the props once the parent component’s data(state) has been updated.
  • When child component receives data(props) from the parent, do not assign it to child component’s state and then use this state to render html. Use the props directly in the render function. The state is set when the component is mounted for the first time. you will have to explicitly unmount and remount the child component to rerender the update state data.
  • child Component can call parent Component’s only those methods that have been passed on to it.
  • If a state is refered in multiple components of different types, the state should be added to the most recent ancestor of the components.

We will create a simple example wherein a User can enter message(eg. ‘Hello’) and a name(eg. ‘Prasad’). The page should display the message and name.

For sake of keeping this post short and simple, I won’t be adding the entire HTML but only the necessary(updated) code. Also, 1) The code sample would contain only the changes for the components. 2) We have the code sample followed by the explanation.

1
2
3
var Greet = React.createClass({});

ReactDOM.render(<Greet />, document.getElementById('container'));

The above code sample will fail because every Component created using React.createClass must implement the render function. The detailed error can be viewed in the browser’s console.

1
2
3
4
5
6
7
8
9
10
11
var Greet = React.createClass({
  getInitialState: function(){
    return { message: 'Hello' };
  },

  render: function(){
    return (
      <h3>{this.state.message}</h3>
    );
  }
});

We updated the Greet component to set some initial value for message and defined the render function to display the message. Since, message has been defined in the same component, hence, message is Greet’s state.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var Greet = React.createClass({
  render: function(){
    return (
      <User message={this.state.message} />
    );
  }
});

var User = React.createClass({
  render: function(){
    return (
      <h3>{this.props.message}</h3>
    );
  }
});

Here, we created a new component named User and passed the Greet’s message to the User. Since, message is passed to User, it’s User’s props.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var User = React.createClass({
  getInitialState: function(){
    return { name: 'Prasad' };
  },

  render: function(){
    return (
      <div>
        <input type='text' name='message' placeholder='Enter message' value={this.props.message}/>
        <input type='text' name='name' placeholder='Enter Name' value={this.state.name}/>
        <h3>{this.props.message} {this.state.name}</h3>
      </div>
    );
  }
});

Here, we added a state named name to User. We are rendering the props message and state name in the User’s render function along with 2 input fields wherein User can enter the custom message and name that he/she wants to be displayed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var User = React.createClass({
  handleNameChange: function(event){
    this.setState({ name: event.target.value });
  },

  render: function(){
    return (
      <div>
        <input type='text' placeholder='Enter message' value={this.props.message} />
        <input type='text' placeholder='Enter Name' value={this.state.name} onChange={this.handleNameChange}/>
        <h3>{this.props.message} {this.state.name}</h3>
      </div>
    );
  }
});

Here, The handleNameChange function receives the event and retrieves the updated value from the target. React provides us a function named setState using which we have updated the state name. If we update the state name without using the setState function, the updated name is not displayed. This is because the setState informs React that an state has been updated and that the DOM needs to be updated.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var Greet = React.createClass({
  updateMessage: function(value){
    this.setState({ message: value });
  },

  render: function(){
    return (
      <User message={this.state.message} handleMessageChange={this.updateMessage}/>
    );
  }
});

var User = React.createClass({
  handleGreetChange: function(e){
    this.props.handleMessageChange(e.target.value);
  },

  render: function(){
    return (
      <div>
        <input type='text' placeholder='Enter message' value={this.props.message} onChange={this.handleGreetChange}/>
        <input type='text' placeholder='Enter Name' value={this.state.name} onChange={this.handleNameChange}/>
        <h3>{this.props.message} {this.state.name}</h3>
      </div>
    );
  }

Here, the function updateMessage will receive the updated value and will update the state message. Also, we need to pass the function to User component. When the user types the message in the textbox, the handleGreetChange function is called. This function retrieves the entered value and passes it handleGreetChange props as a parameter. Since, React implements unidirectional data flow, any components using the props message are updated with the updated value.

So, the entire code is as

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
var Greet = React.createClass({
  getInitialState: function(){
    return { message: 'Hola' };
  },

  updateMessage: function(value){
    this.setState({message: value});
  },

  render: function(){
    return (
    <User message={this.state.message} handleMessageChange={this.updateMessage}/>
    );
  }
});

var User = React.createClass({
  getInitialState: function(){
    return { name: 'Prasad' }
  },

  handleNameChange: function(e){
    this.setState({name: e.target.value});
  },

  handleGreetChange: function(e){
    this.props.handleMessageChange(e.target.value);
  },

  render: function(){
    return (
      <div>
        <input type='text' placeholder='Enter Message' value={this.props.message} onChange={this.handleGreetChange}/>
        <input type='text' placeholder='Enter Name' value={this.state.name} onChange={this.handleNameChange}/>
        <h3>
          {this.props.message} {this.state.name}
        </h3>
      </div>
    );
  }
});

ReactDOM.render(
  <Greet/>,
  document.getElementById('container')
);

The User component defined above can be optimized a bit as below

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var User = React.createClass({
  getInitialState: function(){
    return { name: 'Prasad' }
  },

  handleNameChange: function(e){
    if(e.target.name == "message"){
      this.props.handleMessageChange(e.target.value);
    } else {
      this.setState({name: e.target.value});
    }
  },

  render: function(){
    return (
      <div>
        <input type='text' name="message" placeholder='Enter Message' value={this.props.message} onChange={this.handleGreetChange}/>
        <input type='text' name="name" placeholder='Enter Name' value={this.state.name} onChange={this.handleNameChange}/>
        <h3>
          {this.props.message} {this.state.name}
        </h3>
      </div>
    );
  }
});

Some experiments

Ok. Since we have a working example, lets try to make come changes and check if they work or not. If not, why?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var Greet = React.createClass({
  getInitialState: function(){
    return { message: 'Hola' };
  },

  updateMessage: function(value){
    this.setState({message: value});
  },

  render: function(){
    return (
    <User message={this.state.message} handleMessageChange={this.updateMessage}/>
    );
  }
});

We didn’t pass the handleMessageChange props to the User component. Now, when we enter the message, the same is not being displayed on the page. ie. Every parent component props(state/props/function) that you want to access in the child has to be explicitly passed on to the child Component.

1
2
3
4
5
6
7
8
9
10
11
12
13
var User = React.createClass({
  render: function(){
    return (
      <div>
        <input type='text' name="message" placeholder='Enter Message' value={this.props.message} onChange={this.handleGreetChange}/>
        <input type='text' name="name" placeholder='Enter Name' value={this.state.name} onChange={this.handleNameChange}/>
        <h3>
          {this.state.some} {this.props.message} {this.state.name} {this.props.some}
        </h3>
      </div>
    );
  }
});

Here, we are accessing data(state/props) that has not been defined in any of the components. The above code will still work as expected.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var Greet = React.createClass({
  updateMessage: function(value){
    this.setState({message: value});
    console.log('updated message: '+ this.state.message);
  }
});

var User = React.createClass({
  getInitialState: function(){
    return { name: 'Prasad', message: this.props.message }
  },

  render: function(){
    return (
      <div>
        <input type='text' placeholder='Enter Message' value={this.props.message} onChange={this.handleGreetChange}/>
        <input type='text' placeholder='Enter Name' value={this.state.name} onChange={this.handleNameChange}/>
        <h3>
          {this.state.message} {this.state.name}
        </h3>
      </div>
    );
  }
});

Here, we assigned the message props to a message state and displayed the message state. When we enter the new message in the input box, the Greet’s message state is being updated(can be viewed in browser console) but the same in not reflected on the page. This is because the getInitialState is called only when the component is mounted. Its like an initializer/constructor.

Summerizing, the post is about the the key concepts and their relations to get started with React. Hope it was interesting.

Critics are welcome.

Comments