The Secrets of understanding React.js
In my opinion when a react application getting bigger and complex the main source of confusions comes from not understanding some fundamental JavaScript concepts i.e. Callbacks, Higher Order Functions(HOF), Promises, and Closures.
Note: React components can be Class based or Function based. And there is no much difference conceptually. In this blog post we will be discussing Function component and JavaScript.
Introduction
In simple terms a react component is a JavaScript function which may return a JSX Element (markup which compiles to DOM/HTML element).
function MyComponent() {
const data = 'Some data';
return <div> <h1> { data }</h1> </div>
}
You might be wondering why does React mixes JavaScript and HTML together? Well, the main philosophy behind React is encapsulating UI rendering logic and markup lives together.
In this blog I will be focusing on explaining React components from JavaScript function point of view. Please note Components and JavaScript functions may not be the same but they serve the same purpose.
Let’s see how we would call MyComponent function we defined above in both JavaScript and React way:
MyComponent() // calling JavaScript function
<MyComponent /> // Rendering React component
Essentially both are doing the same thing except the fact that if we are using it as pure JavaScript function JSX syntax is invalid. That is where transpilers like Babel, TypeScript comes in picture where JSX syntax is compiled to pure JavaScript DOM API.
Just to be very clear in this simple(MyComponent) example, this is how we would accomplish the same in JavaScript without React.
//index.html
<html>
<body>
<div id="app"></div>
<script defer type="text/javascript" src="./app.js"></script>
</body>
</html>
// app.js
const myDiv = document.createElement('div');
const myH1 = document.createElement('h1');
myH1.innerHTML = "Some data";
const myApp = document.getElementById('app'); // get html element with id "app"
myDiv.appendChild(myH1);
// note: until this point nothing change in our DOM tree
myApp.appendChild(myDiv);
// now our DIV element which has H1 in it is added to DOM
If we say this much about how JavaScript functions and React components are related, let’s focus on core concepts which are crucial to mastering React.js.
Callbacks
Callbacks are functions the caller passes to callee in parameter which the callee may eventually calls them.
function myCallee(myNum, myCallback) {
if(myNum%2 === 0){
myCallback("Even number");
} else {
myCallback("Odd number");
}
}
function myCaller() {
function processResult(result) {
console.log(result);
}
myCallee(20, processResult)
}
// execute the caller function
myCaller();
As you can see when we try execute `myCaller` function it’s passing two arguments(20 and processResult) which will be assigned to the respective parameters of myCallee(myNum and myCallback). Here are important points to note.
- Once processResult is passed to myCallee as callback, myCallee id deciding when and how to call myCallback in this case processResult.
- myCallee has no idea how the callback function was implemented internally, it just calls it with some value, in this case some string value.
- Since JavaScript has no type checking, if caller was not passing a valid function, the application will fail, since the callee is treating it as a function and calling it back.
- In general caller has no control on how and when the callback is going to be called, it may not call it at all.
Why understanding callback functions is important to better understand React?
Let’s take a look one of the most popular react hook, useEffect.
Usage:
import { useEffect } from "react";
function SomeComponent() {
// some other component logic
useEffect(() => {
// do something
}, []);
// retun some JSX
}
Let’s try simplify it a little bit
function SomeComponent() {
useEffect(() => {
// do some thing
});
}
Were you able to identify the caller, callee, and callback?
SomeComponent: was the caller, which is calling useEffect function.
useEffect: is the callee, which SomeComponent is calling it.
Arrow Function: which was passed in to useEffect as the callback.
We typically pass arrow functions as callback to useEffect. For the sake of clarity I will try simply SomeComponent further:
function doSomeThing() {}
function SomeComponent() {
useEffect(doSomeThing);
}
Now you can clearly see doSomeThing was passed as callback to useEffect function. Also it’s important to note doSomeThing is defined outside the scope of SomeComponent function, that doesn’t really matter. Sometimes it could be a preferred to increase the readability of your component.
SomeComponent: Caller
useEffect: Callee
doSomeThing: Callback
Wait a second, so we said useEffect function will be calling our doSomeThing function which was passed as callback, but when?
I want bring my initial argument here, we were able to understand most of important concepts about how useEffect API works without even knowing why React added it to the library. That is because useEffect is just a pure JavaScript function which takes callback function as parameter which React will eventually calls it.
How does React useEffect hook actually works?
- When Component function(SomeComponent) renders to the DOM first time, it will call useEffect function with the callback function, however useEffect will delay execution of callback function until the component finishes it first render.
- Why: Imagine you want fetch some data from API you don’t want show black screen to users until the data is ready. Instead what useEffect API allows you to do is show uses progress page or some data until the actual data from API ready.
- How: Once the data is ready, DOM or UI will sync to reflect the changes.
- useEffect hook, accepts a dependency array in second parameter, when any change on deps will result re-execution of callback.
- Note: Not providing dep array at all may result in infinite loop, if you want run the callback only once pass empty [] dependency.
- It’s guaranteed useEffect will execute the callback at least once even empty [] deps specified
function ProductComponent(props) {
const [product, setProduct] = useState();
useEffect(() => {
async getProduct(){
const product = await MyAPI.getProductByIdAsync(props.id);
setProduct(product);
}
getProduct(); // useEffect can not take async functions so add wrapper
}, [props]);
return (
<div>
<span> {product.name} <span>
</div>
);
}
// component usage
function MyContainerComponent() {
const id = "1";
return <ProductComponent id={id} />;
}
Conclusion
In this post we tried to discuss how understanding JavaScript callback functions are important to understand React better. This will be a series of posts we will be trying to discuss core React concepts and relating them with JavaScript whenever applicable. You are also welcome to choose any topics you want to discuss.
Next up: Higher Order Functions(HOF) vs Higher Order Components(HOC)