Refreshing React knowledge (part-1)
Rohit Roy
Posted on May 7, 2021
In today's blog, I will share some of the most necessary information to know as a react developer. This is mostly useful if you are a beginner, but it will be an opportunity to refresh your knowledge if you already know them. Let's start.
1. React.js : A JavaScript library
React is a JavaScript library to build user interfaces. It is not a framework like Angular or something else.
Now library is not a fancy thing to describe. It is just a set of some reusable functions. There are hundreds of functions available in React. We call them and manipulate them with some data provided by ourselves. But what to do is the responsibility of itself. We need to only bother how we want our interface to look.
And libraries also gives us full control to manipulate its functions. In other words, libraries are flexible. React started its journey when tech-giant Facebook was facing problems maintaining its code especially the Facebook Ads App back in 2011.
2. DOM API( Document Object Model )
There are three types of things we have to care about when we create any website. They are
- Content
- Presentation
- Behavior
We know content is controlled by HTML, presentation by CSS where the behaviour which is the most important factor for a dynamic website is controlled by JavaScript. Now to make them work altogether, we need something that could be accessed by all of the technology we use(HTML, CSS, JavaScript). What if we create and control all of the 3 concerns using just this 'something'. This will be much easier. From here the idea of the DOM first came. This 'something' we were talking about is nothing but DOM API.
When we create any content for the Internet using HTML, the browsers convert them to a document object which contains all the elements we mentioned in the content as nodes. If we assign different styles to an individual element, this gets also saved in its node in the DOM.
Now here is the good thing. We can access any of the contents and presentations using JavaScript to manipulate them for different conditions, in other words adding behaviour to make the content dynamic.
Let's see an example :
<!DOCTYPE html>
<html>
<head>
<title>DOM Page</title>
</head>
<body>
<h1>The main heading</h1>
<p class="highlight">An interesting summary of this content.</p>
<p>
Some supplementary details to accompany our discussion.
It also has a <a href="#">link</a>.
</p>
<div class="widget">
<div class="foo"></div>
</div>
<table>
<thead>
<tr>
<th>School</th>
<th>Color</th>
</tr>
</thead>
<tbody>
<tr>
<td>UNC Chapel Hill</td>
<td>Carolina Blue</td>
</tr>
<tr>
<td>NC State</td>
<td>Wolfpack Red</td>
</tr>
</tbody>
</table>
</body>
</html>
The above HTML will look like this in the DOM object.
The image and the code are taken from here
3. Components in React
You can think of react components as functions available in all of the programming languages. They have some arguments and they could return some information to the caller. We can create them in such a way that they become reusable.
A react component can describe a smaller part of a UI. And this smaller part is reusable with different arguments. Even we can use a component inside another component. In this case, the prior component will behave like the child component of the later one. Let's see an example of a component :
App.js
import logo from './logo.svg';
import React from 'react';
import './App.css';
import { Profiler, useState } from 'react';
import Text from './Text';
function App() {
return (
<div>
<Text color="red" text="React"></Text>
</div>
);
}
export default App;
Text.js
import React from 'react';
const Text = ({ color, text }) => {
return (
<div style={{ color: color }}>
{text}
</div>
);
};
export default Text;
We are putting Text
component inside App
component. And at last, we are rendering App
in the browser. We will discuss later how this code is working under the hood in a later topic.
We should design or create a component in a way such that it is only responsible for a particular thing. It makes the components reusable and maintainable.
If you notice we start the name of a component with an uppercase letter. It is not a convention! It is necessary. The reason? It will be discussed in the 4th section.
4. React.createElement
If you already have started with react, you could have started a react app using create-react-app
. It sets up an environment to create a react app. It not, here is the link to take a look.
After creating a react app, we will have several files and folders available in the environment.
Let's open the index.js
file now. Inside the file, I have found this...
// Normal
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>
, document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
But where is React.createElement? Ok. Now go to here and copy the above code at the left pane and notice the right pane. This will be like the following...
// transpiled
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render( /*#__PURE__*/React.createElement(React.StrictMode, null, /*#__PURE__*/React.createElement(App, null)), document.getElementById('root')); // If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
We use React.createElement
to create a react element. We have seen that we were creating div
elements in the content section. But if we load them in any browser, the browser has to create a DOM object that contains all the div
elements or other elements as objects, we have called them nodes
.
Here the same thing is happening but in a different complex style. Look at the first code block, firstly we are importing App
which is a component. And then we are using this inside ReactDOM.render
to get rendered on the browser window. If you notice we are not writing HTML
inside return
statement. But it looks like something that follows HTML
syntax. It's called JSX
.
JSX
syntax is easy to understand and code as it has HTML
like syntax. But the second code block is showing what is happening under the hood. We are creating react element object using React.createElement
. This conversion is called transpiling
. create-react-app
uses Babel
to transpile any JSX
. We could also create these objects directly. However, it is more complex and hard to maintain. So thanks to transpilers to give us some comfort.
React.createElement
could have many arguments ( when we want to use child components ).
React.createElement(Another react element/HTML tag, attributes, content of the DOM element(optional), ....child components....)
It may be confusing that we are importing App
component to use it as an argument in the createElement
in index.js
. Let's see what App
component returns to the index.js
by using Babel
tool.
function App() {
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Text, {
color: "red",
text: "React"
}));
}
null means this element doesn't have any attribute.
Hence the App
component is returning a react element. Now at the time of processing App
component, there is another argument that is the only child component. So again let's get into this component and see what it returns( after transpiling obviously! )
const Text = ({
color,
text
}) => {
return /*#__PURE__*/React.createElement("div", {
style: {
color: color
}
}, text);
};
Look carefully! This makes sense. We are passing some attributes with the Text
component call also as the second argument and in the Text
component we are grabbing those attributes and use them in the second arguments of a core DOM
element( div ).
In the previous, we left a question without answering. Why must we start a component name with an uppercase letter?
The reason is the way a transpiler parses and differentiate between a core DOM element and a react component. All the core DOM elements start with a lowercase letter( div, a, p etc ). Transpilers like Babel
parses core DOM
elements as strings whereas react components as objects.
Now in the
Text
component, we used anothercreateElement
method to create another element and passed some arguments also. But where will these attributes be passed to? Will they go to their last destination, DOM API? Continue to read! We will build the idea and try to get the answer.
Before going any further, let's know some informative things about JSX
.
5. JSX
: Make things familiar
Yes! It just makes the syntax familiar to us. As we saw earlier instead of writing react components using React.createElement
, we can use JSX which has a syntax very similar to HTML. But before using this code, we have to transpile them using any transpiler like Babel
.
Let's learn a few things about JSX :
JSX tag (like
HTML
) determines the type of the React element. The capitalized name means there should be a user-defined component in the scope. These capitalized name will refer to the component after get compiled.All user-defined components must be capitalized. Let's see why again,
// Text from App
React.createElement(Text, {
color: "red",
text: "React"
});
// div inside Text
React.createElement("div",{
style: {
color: color
}
}, text);
So JSX will parse user-defined components as reference whereas normal DOM
elements as a string. If we don't follow this rule, there will be an error for sure.
- We can write an expression in JSX using curly braces. Inside curly braces, we can call another component, write javascript expressions etc.
<Component>Hello React!</Component>
// using string literals as children
We can also show nested components in JSX :
<ParentComponent>
<ChildComponent/>
<ChildComponent />
</ParentComponent>
- If we want to use functions inside JSX expression, we have to make sure they return any react elements.
const names = ['naiklein', 'sasha', 'puffea', 'numeic'];
<Component>
{
names.map(name => <div> {name} </div>)
}
</Component>
Learn more about JSX : JSX
6. Props: Send information
As we said earlier, components are like normal functions in JavaScript. We can pass some information through arguments and they can return based on this information. Another thing to remember that this data passing only happen from the parent component to the child components. Though we can do the reverse by applying some trick, for now, let's only care for the first thing.
// From the App.js we have sent colour and text attributes to Text.js
...
<Text color="red" text="React"></Text>
...
// let's see how Text.js get these data
...
const Text = (props) => {
return (
<div style={{ color: props.color }}>
{props.text}
</div>
);
};
...
For normal function, we write Text = (color, text)
. But here we have written props
because in class components of react there is a built-in object that holds the passed arguments. But till now, we have only written functional components. They follow the same processing of the arguments: holding all of them in an object, but the object is not named props
. Hence we can give any name to this object. Though props
should be used as a convention ( taken from class components ).
We can also use object destructuring to parse attributes from props.
const Text = ({color, text}) => {
return (
<div style={{ color: color }}>
{text}
</div>
);
};
Learn more about props from here here.
7. Virtual DOM API
In the 4th section we have left a question about the final destination of core DOM
elements or where do they get passed at last?
The browser window updates itself every time if there is a single update on the DOM tree. It is not necessary, it should update only the particular part of the DOM that just has been updated at any particular time. To prevent this performance issue, react introduces another DOM tree that holds all the available objects ( aka react elements ) and stores this tree in the memory.
Hence virtual DOM tree is just a copy of the real DOM tree. So when we add a new element through JSX and made any changes in the JSX the whole virtual DOM gets updated. So we are actually
8. Tree reconciliation: make things faster
At the time of using react, there is something different that happens when we update any component( any of its data ). When we do any DOM updates, the whole DOM gets updated by the browsers and repainted in the browser. This is a lengthy process as the browser has to analyze the whole DOM and reload the page with new changes ( though we have only changed a little part of the DOM, it don't care! ).
This expensive operation makes the user experience slow. React finds the solution by storing and analyzing the changes every time we try to render a component. When we try to render some a component, firstly it will create a new version of the whole tree but virtually or in the memory. Then it will analyze the current tree with the tree that already exists and finds the changes and define the minimum part of the tree to render in the browser. This is called 'Tree reconciliation'.
After defining the changes, it will update them in the real browser DOM, not the whole DOM. Let's see how :
Firstly go to index.html
file of your react project and add two div's like the following :
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
// by default there will be only one div element where we will render our react component( that contains all of the child components )
<div id="root1"></div>
<div id="root2"></div>
</body>
And then we are going to create the same element with different methods. One with core DOM methods and the other with React render and createElement
method in index.js
.
document.getElementById('root1').innerHTML = `
<div>
Hello React
<input />
<pre>${new Date().toLocaleTimeString()}</pre>
</div>
`;
ReactDOM.render(
React.createElement(
"div",
null,
"Hello React ",
React.createElement("input"),
React.createElement('pre', null, new Date().toLocalTimeString())
),
document.getElementById('root2'),
);
Let's see the output :
ReactDOM.render
accepts two arguments. The first one for a react element and the last one for setting the first react element to a specified DOM node ( available at index.html
). Now we are going to do update the time continuously after every 1 second.
const update = () => {
document.getElementById('root1').innerHTML = `
<div>
Hello React
<input />
<pre>${new Date().toLocaleTimeString()}</pre>
</div>
`;
ReactDOM.render(
React.createElement(
"div",
null,
"Hello React ",
React.createElement("input"),
React.createElement('pre', null, new Date().toLocaleTimeString())
),
document.getElementById('root2'),
);
}
setInterval(() => {
update()
}, 1000);
If we now notice what is happening to the DOM, we have to inspect.
Firefox Inspector is highlighting the updated element consistently. But if you look carefully, in the case of react element, only the date string is updating. On the other hand, the whole root1
element is updating after every 1s. The reason is that react is only updating the necessary element or component out of the whole DOM ( tree reconciliation ).
Posted on May 7, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
October 16, 2024