ReactJs Blog — The one with the single HTML file
Hi Folks this my first blog ever and today i am going to write about ReactJs and build a very basic app with in depth explanation of each and every step and each and every bit of code.
If you have a hard time understanding something w.r.t this tutorial or React in general, comment and i will try to help as much as i can.
Who is this blog for???
(I have started with many blogs and later came to the conclusion that okay boss this is not my cup of tea). So if you don’t want to waste your time do read the line below.
Anyone who knows JS & HTML and can write even basic JS & HTML. ( I will try to assume that the reader is like me who had no knowledge of React and lil knowledge of JS and was thrown into the pit to fight the mighty React Framework. )
Note: This is very basic example to help newbies start their React journey.
What do you need before hand to get this project/tutorial running.
- A browser (Chrome preferable).
- A text editor
So React is just the View part of the MVC framework. There are various techs out there which can provide the you the model and controller bit but for this tutorial that is out of scope.
For this tutorial we will have just one HTML file only.
Create a folder My_first_React, in this folder create a file TODO.html
Let’s start with a simple hello world.
Paste the code below in the HTML you just created.
<!DOCTYPE html>
<html>
<head>
<meta charset=”UTF-8" />
<title>Hello React!</title>
<script src=”https://cdn.jsdelivr.net/react/0.14.0-rc1/react.js"></script>
<script src=”https://cdn.jsdelivr.net/react/0.14.0-rc1/react-dom.js"></script>
<script src=”https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
</head>
<body>
<div id=”example”></div>
<script type=”text/babel”>ReactDOM.render(
<h1>Hello World</h1>,
document.getElementById(‘example’)
);
</script>
</body>
</html>
So what just happened???
If all went write you must be seeing a big Hello World on your screen.
So above we have a normal HTML file with all the common tags. Let us look at the major one’s
<script src=”https://cdn.jsdelivr.net/react/0.14.0-rc1/react.js"></script>
<script src=”https://cdn.jsdelivr.net/react/0.14.0-rc1/react-dom.js"></script>
<script src=”https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
First one imports ReactJs code, Second one imports ReactDOM (React was split into React and ReactDOM sometime back) and last one is Babel (will explain in a sec what that is).
<script type=”text/babel”>
ReactDOM.render(
<h1>Hello World</h1>,
document.getElementByID(‘example’)
);
</script>
This is where the main MAGIC happens
As you can see we are using HTML (JSX) inside the script tag, where JS should be written. So how does that work??
This is where Babel comes into play. Babel does a lot of things ( which are out of scope for this tutorial ) but all you need to know is that it converts HTML or JSX to JS code which is then rendered into the div element with id ‘example’. (to how this JSX looks like in JS form go the the link https://babeljs.io/repl/#?evaluate=true&lineWrap=false&presets=es2015%2Creact&code=%3Ch1%3Ehello%20world%3C%2Fh1%3E).
Let us now break our code into React Component as it will be difficult to write all the code in that ReactDOM function.
Make a new Component with the name Hello.
<script type=”text/babel”>
var Hello = React.createClass({
render: function() {
return(
<h1> Hello World </h1>
);
}
});
ReactDOM.render(
<Hello />,
document.getElementById(‘example’)
);
</script>
React.createClass is used to create a component which must have one render method which returns JSX. If you open this in your browser you will see Hello World.
Now let me explain the two main things in React i.e. props and state.
props : props is basically data which a parent passes to its child.
VERY VERY IMPORTANT POINT to know is that PROPS ARE IMMUTABLE i.e props should not be changed, you should not edit props or modify them in any way they are like const. DO NOT, I REPEAT DO NOT MUTATE PROPS. So you must be thinking what do i do when i have to change or mutate some value that is in props. This is where state comes into picture.
state : state is nothing but data local to a component which it can manipulate mutate or do anything as it may please. So to mutate data in props you copy it or append it to state and then use it in any form you like.
example:
<script type=”text/babel”>
var Hello = React.createClass({
getInitialState: function() {
return({
‘author’: ‘shashank’
});
},
render: function() {
return(
<h1> {this.props.code} </h1>
<h2> {this.state.author} </h2>
);
}
});
ReactDOM.render(
<Hello code={‘my first react code’} />,
document.getElementById(‘example’)
);
</script>
Objects are to be passed into the Hello Component, you can pass as many props you like, all of them will be available in this.props object in that component. Don’t worry about that getInitialState method it will explained in a little while.
Run this code. You will see nothing on the screen.
Open you console, there you will see an error.
What it means is that if you have more than one HTML/JSX tag they should be wrapped in one parent HTML/JSX tag. ( Remember it looks like HTML but it is actually JSX) (this is a very common React error)
Wrap your h1 and h2 tags into one div tag, now open it in the browser. You will see something like this.
Coming to that getInitialState method. React has many Lifecycle methods, getInitialState is one of those methods. (For those who don’t know what are lifecycle methods, these are basically methods that are invoked during different phases of this Component when it is called, like init method or destroy method etc.)
React has many life cycle methods but you should know about the four main ones i.e.
- getInitialState
- componentWillMount.
- componentDidMount
- componentWillUnmount
They are run in the order they are listed and work as the name suggests like componentWillMount is invoked just before is component is mounted and componentDidMount is run just after component is mounted (mounted means on to the DOM, that ‘example’ div tag.)
If you understand the concept of state, props and lifecycle methods you are almost done with React, rest is just getting accustomed to the syntax and few minor things.
With the basics covered let us dive in to React a little bit more by making a simple TODO app with add and remove functionalities.
Points to note.
- The code will be written in ES5 syntax. There will be one file with the whole code in ES6 at the end of this tutorial. (For those who don’t know what ES5 and ES6 are in layman’s term there is syntax difference on how you write code).
Let us start by making a TODO component with a simple input tag using which you can enter your todos.
<script type=”text/babel”>
var TODO = React.createClass({
getInitialState: function() {
return ({
‘TODOS’: []
});
},
render: function() {
return (
<div>
Add a todo: <input type=”text” name=”todo” ref=”todo” /><input type=”submit” value=”submit” />
</div>
);
}
});
ReactDOM.render(
<TODO />,
document.getElementById(‘example’)
);
</script>
you will see something like this.
Did you notice that there is an attribute “ref”, don’t get puzzled on what attribute is this, is it HTML 5 or HTML 4 coz it’s none of those. It is a special attribute provided by React for easily extracting the DOM element. We will see an example soon.
Also we have TODOS, an array in which we will store our TODOs (in the state of our component)
When you enter your todo and click submit nothing will happen. Don’t worry we have not added that functionality yet!
Let us go ahead and add that.
What we will do is onClick call a function which will get this value and insert it into the TODOS array that we just created.
<script type=”text/babel”>var index = 0;var TODO = React.createClass({getInitialState: function() {
return ({
‘TODOS’: []
});
},addTODO: function() {
var TODOItem = this.refs.todo.value;
if(TODOItem.length != 0) {
var temparr = this.state.TODOS.concat({'id': index++, 'todo_item': TODOItem});
}
this.refs.todo.value = “”;
console.log(temparr);
},render: function() {
return (
<div>
Add a todo: <input type=”text” name=”todo” ref=”todo” />
<input type=”submit” value=”submit” onClick={this.addTODO} />
</div>);
}
});ReactDOM.render(
<TODO />,
document.getElementById(‘example’)
);</script>
Here on line 4 we have added a global variable named ‘index’ which will help us assign something like a primary key (in DB) or unique id for the TODOs that you will create.
line 30 we have attached the onClick event to the Submit button.
and on line 17 we have added the addTODO function which works as follows:
with the help of ref we get the DOM element i.e. our input tag and using ‘.value’ we get the value out of it and check if it is non empty then we push it into the TODOS array as an object (Why as an object will get to that soon).
Open your console and every time you insert a new todo you can see it being added to the TODOS array.
POINT TO NOTE: state is not persistent, i.e. once you refresh the browser TODO list will be gone. (To make it persistent we need to add todos that we created to a file or DB which is out of the scope of this tutorial).
Next we need to display this TODO List on your browser let’s go ahead and do that.
<script type=”text/babel”>var index = 0;var TODO = React.createClass({getInitialState: function() {
return ({
‘TODOS’: []
});
},addTODO: function() {
var TODOItem = this.refs.todo.value;
if(TODOItem.length != 0) {
var temparr = this.state.TODOS.concat({'id': index++, 'todo_item': TODOItem});
this.setState({TODOS: temparr});
}
this.refs.todo.value = “”;
},render: function() {
var todoList = this.state.TODOS.map(function(todoItem) {
return (
<li>{todoItem.todo_item}</li>
);
});
return (
<div>
Add a todo: <input type=”text” name=”todo” ref=”todo” />
<input type=”submit” value=”submit” onClick={this.addTODO} />
<ul>
{todoList}
</ul>
</div>
);
}
});ReactDOM.render(
<TODO />,
document.getElementById(‘example’)
);</script>
what we have done is called this.setState method. This method basically has two purpose first is to set the state and second to tell the component that there has been a change in the state and that the component should re-render.
map is just a simple iterator, it is like a loop (for or while or do while etc.). It takes in an Array of Objects and iterates over the array and gives you the individual objects that in that Array. (Reason why we inserted objects in the TODOD array).
NOTE: Component will re-render when there is a change in state (you have to call setState to let react know there has been a state change) and when it receives props.
In the browser if you try to add something now it will be added.
Congratulations you have made the add TODO part!!!
Now open the console do you see a Warning like this.
Well this is because React assigns a unique key to every element when it creates the DOM but here when we use map iterator we are creating the DOM on reacts behalf so to say and it is our job to assign a unique key to the parent element that we render in the map.
This is how you do it
var todoList = this.state.TODOS.map(function(todoItem) {
return (
<li key={todoItem.id}>{todoItem.todo_item}</li>
);
});
we just used the id of each todo which is unique. Inspect element and go to this node i.e. the li element you just added and see that a custom key something $0 will be there ($key means it is a user defined key)
Refresh and now you will see that the error is not there anymore.
Now let us begin the delete Todo part. As no one likes to do a TODO :) :p
We will add a cross (X) wit each TODO which will remove the TODO.
var todoList = this.state.TODOS.map(function(todoItem, i) {
return (
<li key={todoItem.id}>{todoItem.todo_item} <span style={{‘color’: ‘red’}}>x</span></li>
);
});
(Please note only this part of the code is changed rest is same.)
We have added a span because it is inline by default and given it a color RED. In React style is passed as object therefore the extra pair of curly braces.
Like before we have not added any functionality hence clicking on the cross will do nothing. Let’s go ahead and add that as well.
<script type=”text/babel”>
var index = 0;
var TODO = React.createClass({
getInitialState: function() {
return ({
‘TODOS’: []
});
},
addTODO: function() {
var TODOItem = this.refs.todo.value;
if(TODOItem.length != 0) {
var temparr = this.state.TODOS.concat({'id': index++, 'todo_item': TODOItem});
this.setState({TODOS: temparr});
}
this.refs.todo.value = “”;
},
removeTODO: function(id) {
console.log(id);
},
render: function() {
var todoList = this.state.TODOS.map(function(todoItem, i) {
return (
<li key={todoItem.id}>{todoItem.todo_item} <span onClick={this.removeTODO.bind(this, todoItem.id)} style={{‘color’: ‘red’}}>x</span></li>
);
}.bind(this));
return (
<div>
Add a todo: <input type=”text” name=”todo” ref=”todo” />
<input type=”submit” value=”submit” onClick={this.addTODO} />
<ul>
{todoList}
</ul>
</div>
);
}
});
ReactDOM.render(
<TODO />,
document.getElementById(‘example’)
);
</script>
So we have added a function named removeTODO which receives an id (which is going to be the index of the todo object to be removed in the TODOS array) and for now just logs it onto the console. We also added an onClick event to the span [ if you don’t understand that bind thing just check it out , basically the function is out of scope for the elements inside the map so we manually bind the ‘this’ object to reference the scope of the outside world of map (scope of TODO component ) ]
Now open the console, add a TODO and click the red cross. You can see that the id is being printed in the console.
Now we will add the functionality to remove the TODO and render the list again.
<script type=”text/babel”>var index = 0;var TODO = React.createClass({getInitialState: function() {
return ({
‘TODOS’: []
});
},addTODO: function() {
var TODOItem = this.refs.todo.value;
if(TODOItem.length != 0) {
var temparr = this.state.TODOS.concat({'id': index++, 'todo_item': TODOItem});
this.setState({TODOS: temparr});
}
this.refs.todo.value = “”;
},removeTODO: function(id) {
var temparr = this.state.TODOS;
temparr.splice(id, 1);
this.setState({TODOS: temparr});
},render: function() {
var todoList = this.state.TODOS.map(function(todoItem, i) {
return (
<li key={todoItem.id}>{todoItem.todo_item} <span onClick={this.removeTODO.bind(this, i)} style={{‘color’: ‘red’}}>x</span></li>
);
}.bind(this));
return (
<div>
Add a todo: <input type=”text” name=”todo” ref=”todo” />
<input type=”submit” value=”submit” onClick={this.addTODO} />
<ul>
{todoList}
</ul>
</div>
);
}
});ReactDOM.render(
<TODO />,
document.getElementById(‘example’)
);</script>
Line 27 and 28 we remove the TODO and setState.
With this we have created the TODO App we set out to make!!!!!!!
Congratulations you have now good enough knowledge of React to start you own project, keep at it.
ES6 syntax code:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="https://cdn.jsdelivr.net/react/0.14.0-rc1/react.js"></script>
<script src="https://cdn.jsdelivr.net/react/0.14.0-rc1/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">let index = 0;class TODO extends React.Component {constructor(props) {
super(props);
this.state = {TODOS : []};
}addTODO() {
let TODOItem = this.refs.todo.value;
if(TODOItem.length != 0) {
let temparr = this.state.TODOS.concat({'id': index++, 'todo_item': TODOItem})
this.setState({TODOS: temparr});
}
this.refs.todo.value = "";
}removeTODO(id) {
let temparr = this.state.TODOS;
temparr.splice(id, 1);
this.setState({TODOS: temparr});
}render() {
let todoList = this.state.TODOS.map(function(todoItem, i) {
return (
<li key={todoItem.id}>{todoItem.todo_item}
<span onClick={this.removeTODO.bind(this, i)} style={{'color': 'red'}}>x</span>
</li>
);
}.bind(this));
return (
<div>
Add a todo: <input type="text" name="todo" ref="todo" />
<input type="submit" value="submit" onClick={this.addTODO.bind(this)} />
<ul>
{todoList}
</ul>
</div>
);
}
};ReactDOM.render(
<TODO />,
document.getElementById('example')
);</script>
</body>
</html>
More Tutorials to follow. Keep checking this space out.
As an exercise try adding the completed feature in this TODO App.