Let's know about the JavaScript concept and react hook

Muktarul Khan Akash
22 min readNov 5, 2020

This post covers some basic concepts in JavaScript. Every JavaScript programmer must know and master these.

1. JavaScript Prototype

Let me explain the various way of creating objects in JavaScript. One of the ways to create objects in JavaScript is the constructor function. Consider the below constructor function:

function Bike(model,color){
this.model = model,
this.color = color,this.getDetails = function(){
return this.model+' bike is '+this.color;
}
}
var bikeObj1 = new Bike('BMW','BLACK');
var bikeObj2 = new Bike('BMW','WHITE');console.log(bikeObj1.getDetails()); //output: BMW bike is BLACK
console.log(bikeObj2.getDetails()); //output: BMW bike is WHITE

In the above example, we are creating two objects, bikeObj1, bikeObj2 using a constructor. In JavaScript, every object has its own methods and properties. In our example, two objects have two instances of the constructor function getDetails(). It doesn’t make sense to have a copy of getDetails doing the same thing.

Instead of using a copy of instances, we are going to use the prototype property of a constructor function.

Prototype

When an object is created in JavaScript, the JavaScript engine adds a __proto__ property to the newly created object which is called dunder proto. dunder proto or __proto__ points to the prototype object of the constructor function.

function Bike(model,color){
this.model = model,
this.color = color
}Bike.prototype.getDetails = function(){
return this.model+" bike is "+this.color;
}
var bikeObj1 = new Bike(‘BMW’,’Black’);console.log(bikeObj1.getDetails());

The bikeObj1 object, which is created using the Bike constructor function, has a dunder proto or __proto__ property which points to the prototype object of the constructor function Bike.

In the below code, both bikeObj1 it's dunder proto or __proto__ property and Bike.prototype property is equal. Let’s check if they point at the same location using === operator.

console.log(bikeObj1.__proto__ === Bike.prototype );//output : true

TK [Not sure what the author intends to say here] So using prototype property, how many objects are created functions are loaded into memory only once and we can override functions if required.

2. JavaScript(ES6) Class

JavaScript classes, introduced in ES6, are primarily syntactical sugar over JavaScript’s existing prototype-based inheritance. The class syntax does not introduce a new object-oriented inheritance model to JavaScript. In early ES5 using function expression.

existing Prototype based inheritance:

function Bike(model,color) {
this.model = model;
this.color = color;
}

Bike.prototype.getInfo = function() {
return this.color + ' ' + this.model+ ' bike';
};

Defining classes:

Classes are in fact “special functions”, and just as you can define function expressions and function declarations, the class syntax has two components: class expressions and class declarations.

ES6 class:

class Bike{
constructor(color, model) {
this.color= color;
this.model= model;
}
}

Benefits of Using class

  • Convenient, self-contained syntax.
  • A single, canonical way to emulate classes in JavaScript. Prior to ES6, there were several competing implementations in popular libraries.
  • More familiar to people from a class-based language background.

3. IIFE

What is an IIFE in JavaScript?

IIFE (Immediately Invoked Function Expression) is a JavaScript function that runs as soon as it is defined.

(function () 
{// logic here })
();

Your first encounter may seem quite confusing but actually, the pattern is simple. The pattern is immediately invoked function expression.

JavaScript functions can be created either through a function declaration or a function expression. A function declaration is the “normal” way of creating a named function.

A function created in the context of an expression is also a function expression. The key thing about JavaScript expressions is that they return values.

In both cases above the return value of the expression is the function.
That means that if we want to invoke the function expression right away we just need to tackle a couple of parentheses on the end. This brings us back to the first bit of code we looked at:

(function () 
{ var foo = “hello”;
console.log(foo);
})
();
console.log(foo); //Error: foo is not defined

The primary reason to use an IIFE is to obtain data privacy. Because JavaScript’s var scopes variables to their containing function, any variables declared within the IIFE cannot be accessed by the outside world.

4.understanding Scope:

A simple definition for a scope in JavaScript:

Scope is the accessibility of variables, functions, and objects in some particular part of your code during runtime. In other words, scope determines the visibility of variables and other resources in areas of your code.

As per the above definition of Scope, So, the point in limiting the visibility of variables and not having everything available everywhere in your code.

the scope is defined in the main two ways,

  • Global Scope
  • Local Scope
var greeting='Welcome to blog';
(function(){
console.log(greeting); //Output: Welcome to blog
})();

consider above code greeting variable should be global scope, it can access inside the function,

(function(){var greeting = 'Welcome to blog';
console.log(greeting); //Output: Welcome to blog
})();console.log(greeting); //Output:Reference-Error greeting not defined

In the above code for local scope,

In scope level variables in JavaScript ES6 has updated hoisting variable let, var, const type check with that, In order to learn the scope, you need to understand hoisting also.

for more refer to this site https://scotch.io/tutorials/understanding-scope-in-javascript

5. JavaScript Closures

What is Closure?

A closure is the combination of a function and the lexical environment within which that function was declared.

A closure is an inner function that has access to the outer (enclosing) function’s variables — scope chain. The closure has three scope chains: it has access to its own scope (variables defined between its curly brackets), it has access to the outer function’s variables, and it has access to the global variables.

Let see a closure example below:

function User(name){  var displayName = function(greeting){
console.log(greeting+' '+name);
}
return displayName;
}var myFunc = User('Raj');myFunc('Welcome '); //Output: Welcome Raj
myFunc('Hello '); //output: Hello Raj

In this code, We have an outer function User() which returns an inner function as displayName(),

The inner function will have access to the variables in the outer function scope, even after the outer function has returned.

6. The Module Pattern

In JavaScript, a module is a small unit of independent, reusable code. Modules are the foundation of many JavaScript design patterns and are critically necessary when building any non-trivial JavaScript-based application.

JavaScript module export as the value rather than define a type, as JavaScript JavaScript module can export an object, Modules that export a string containing an HTML template or a CSS stylesheet are also common.

JavaScript doesn’t have private keywords but we can achieve private methods and properties using closures.

var myModule = (function() {
'use strict';

var _privateProperty = 'Hello World';

function _privateMethod() {
console.log(_privateProperty);
}

return {
publicMethod: function() {
_privateMethod();
}
};
}());

myModule.publicMethod(); // outputs 'Hello World'
console.log(myModule._privateProperty); // is undefined
protected by the module closure
myModule._privateMethod(); // is TypeError protected by the module closure

these modules can have exported to the other JS files using the export keyword,

//myMOdule.js fileexport default myModule;

modules can import to another JS file

//second.js file 
import myModule from ‘./myModule’;

Why do we need to use a Module?

There are a lot of benefits to using modules in favor of a sprawling,

some are,

  1. maintainability
  2. reusability
  3. Namespacing

7. Hoisting:

In JavaScript what its hoisting means,

Hoisting is a JavaScript mechanism where variables and function declarations are moved to the top of their scope before code execution.

It’s actually a definition for hoisting,

In simple explanation for Hoisting with code,

console.log(Hoist);
var Hoist = ’The variable Has been hoisted’;
//output : undefined//

actually, JavaScript has hoisted the variable declaration. This is what the code above looks like to the interpreter

var Hoist;
console.log(Hoist);
Hoist = ’The variable Has been hoisted’;

JavaScript only hoists declarations, not initialization.

Inevitably, this means that no matter where functions and variables are declared, they are moved to the top of their scope regardless of whether their scope is global or local.

so this also match for a variable of function-level scope also hoisted

Next thing you need to know about Hoisting,

  1. let, var, const keyword in JavaScript (ES6)
  2. Hoisting Functions
  3. Hoisting classes

For more refer to this site Understating-Hoisting-in-JavaScript

8. Currying:

Explaining currying about with examples in JavaScript

Currying is a technique of evaluating the function with multiple arguments, into a sequence of functions with a single argument.

In other words, when a function, instead of taking all arguments at one time, takes the first one and return a new function that takes the second one and returns a new function which takes the third one, and so forth until all arguments have been fulfilled.

consider below example code:

var add =   function (a){
return function(b){
return function(c){
return a+b+c;
}
}
}
console.log(add(2)(3)(4)); //output 9
console.log(add(3)(4)(5)); //output 12

this currying achieving through closures, so above program variables a,b private properties of the parent function

Why Useful Currying?

Mainly It helps to create a higher-order function. It is extremely helpful in event handling.

How to convert an existing function to a curried version?

The curry function does not exist in native JavaScript. But a library like lodash makes it easier to convert a function to a curried one.

9. Memoization:

Memoization is a programming technique that attempts to increase a function’s performance by caching its previously computed results. Because JavaScript objects behave like associative arrays, they are ideal candidates to act as caches. Each time a memoized function is called, its parameters are used to index the cache. If the data is present, then it can be returned, without executing the entire function. However, if the data is not cached, then the function is executed, and the result is added to the cache.

The function is an integral part of the programming, they help to modularity and reusable to our code, as per the above definition memoization is an optimizing our code

const memoizedAdd = () => {
let cache = {};
return (value) => {
if (value in cache) {
console.log('Fetching from cache');
return cache[value];
} else {
console.log('Calculating result');
let result = value + 10;
cache[value] = result;
return result;
}
}
}// returned function from memoizedAdd
const newAdd = memoizedAdd();
console.log(newAdd(9)); //output: 19 calculated
console.log(newAdd(9)); //output: 19 cached

from the above code, you can understand memoization.

11. The apply, call, and bind methods:

In traditionally JS have object, Properties, and methods, so each object has properties and methods.

In JavaScript, we can do some magic using call, apply, bind methods,

JavaScript Call/Apply/Bind Method

Consider the above image Object1 can have its own Properties and Object2 can have its own property, so we can write a common method for these objects and use within that using call/apply/bind method. I hope you now you can understand why the call/applies/bind method using.

let’s look at the difference and code for the Call/Apply/Bind method

1.Call method:

consider below code, obj have the property of num, using call method we can bound obj to add function,

var obj={
num : 2
}
var add = function(num2,num3,num4){
return this.num + num2 + num3 + num4;
}
var arr=[3,4,5];//Call method
console.log(add.call(obj,3,4,5)); //Output : 14//Apply method
console.log(add.apply(obj,arr)); //Output : 14//bind Method
var bound = add.bind(obj);
console.log(bound(3,4,5)); //output : 14

2.Apply Method

The same way the Apply method also works but the only difference is using the apply method the passing arguments could be an array, refer to below code.

3.Bind Method:

bind method returns a method instance instead of result value, after that need to execute a bound method with arguments.

In the above Code simply explain how to use the call/Apply/Bind method with an argument.

11. Polymorphism in JavaScript:

Polymorphism is one of the tenets of Object-Oriented Programming (OOP). It is the practice of designing objects to share behaviors and to be able to override shared behaviors with specific ones. Polymorphism takes advantage of inheritance in order to make this happen.

let’s look at the sample code for an override a function in JavaScript

var employee = new Employee('raja');//override function
//this method going to executeEmployee.prototype.getDetails = function() {
return this.name.toUpperCase();
}console.log(employee.getDetails()); //outPut: RAJA//object and prototype function
function Employee(name) {
this.name = name;
}Employee.prototype.getDetails = function() {
return this.name;
}

In the above simple program prototype-based method for an Employee constructor function has to override by another prototype function as return the Name as uppercase.

So we can override a function in different Scope, and also possible for method overloading, JS doesn’t have method overloading native but still, we can achieve.

Still few concepts are in oops, which are all are Method overloading, Abstraction, Inheritance in JavaScript.

12. Asynchronous Js :

In JavaScript Synchronous and asynchronous are code execution Pattern,

In JavaScript Code Execution done In two separate ways:

  1. Browser JS Engine (popular V8 JS Engine)
  2. NodeJs V8 Engine

Browser JS Engine parse Html file and executes JavaScript by three patterns,

  1. synchronous
  2. Asynchronous
  3. defer
index.html<script src='index.js'>           //default Synchronous<script async src='index.js'>      //parse as Asynchronously<script defer src='index.js'>      //parse as deferred

while browser JS Engine parsing HTML file if <Script > tag occurs means there will be blocking, so how it gets execute JavaScript Code for that above three patterns.

  1. If synchronous <script > tag occurs, JS engine will download the code and execute that code and after that only parsing the below HTML code, generally Synchronous is a blocking script execution.
  2. If Asynchronous <script async> tag occurs, while downloading the code JS engine will parse the HTML, and once If JS code gets downloaded pause the parsing and back into the JS Code Execution, generally Asynchronous is a Non-blocking script execution.
  3. If Asynchronous <script defer> tag occurs, JS Engine will parse the all HTML code and after that only executes the JS Code,

NodeJS V8 Engine:

NodeJS V8 engine executes its JavaScript Code as single-threaded based on the Event loop!….. know more about NodeJS execution from the below links.

Event Loop and NodeJS

Synchronous and Asynchronous Paradigm

13. Callback Function:

Callback function definition:

A reference to executable code, or a piece of executable code, that is passed as an argument to other code.

From the above definition, the callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action.

function greeting(name) {console.log('Hello ' + name);
}function processUserInput(callback) {
//var name = prompt('Please enter your name.');
name = 'raja';
callback(name);
}
processUserInput(greeting); //output Hello Raja

In this ab above program, function greeting passed as an argument to the process user input function, so I hope you now understood callback function in JavaScript.

14. Understand Promises :

Promise definition:

The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.

A promise represents the result of the asynchronous function. Promises can be used to avoid chaining callbacks. In JavaScript,

so whenever JavaScript Code Execute as Asynchronously, need to handle an operation as one of the ways would be using promises.

Promises, have been around quite a while and are defined by a spec called Promise/A+. ES6 has adopted this spec for its Promise implementation; but there are other Promise libraries out there such as Q, Bluebird, RSVP, and others that adhere to this spec and offer other features on top of it.

A Promise is in one of these states:

  • pending: initial state, neither fulfilled nor rejected.
  • fulfilled: meaning that the operation completed successfully.
  • rejected: meaning that the operation failed.
var promise1 = new Promise(function(resolve, reject) {
isDbOperationCompleted = false;
if (isDbOperationCompleted) {
resolve('Completed');
} else {
reject('Not completed');
}
});promise1.then(function(result) {
console.log(result); //Output : Completed
}).catch(function(error) {
console.log(error); //if isDbOperationCompleted=FALSE
//Output : Not Completed
})

consider above code for a sample promise assume like doing DB Operation as asynchronously, In that promise Object arguments as two function resolve and reject,

whenever we execute that promise using .then() and .catch() as callback function In order to get resolve or reject values

To learn more about Promise.

15. Async & Await:

Babel now supporting async/await out of the box, and ES2016 (or ES7) just around the corner, async & await basically just syntactic sugar on top of Promises, these two keywords alone should make writing asynchronous code in Node much more bearable.

In JavaScript Asynchronous pattern handled in various versions,

ES5 -> Callback

ES6 -> Promise

ES7 -> async & await

However, what a lot of people may have missed is that the entire foundation for async/await is promised. In fact, every async function you write will return a promise, and every single thing you await will ordinarily be a promise.

async function getUserDetail() {
try {
let users = await getUsers();
return users[0].name;
} catch (err) {
return {
name: 'default user'
};
}
}

Above code for a simple async & await,

Good, async/await is a great syntactic improvement for both nodejs and browser programmers. Comparing to Promises, it is a shortcut to reach the same destination. It helps the developer to implement functional programming in JavaScript, and increases the code readability, making JavaScript more enjoyable.

In order to understand Async&Await, you just need to understand promises.

In Async & Await lot more stuff there, Just play around with it.

conclusion:

As far, In this Post, we have covered many important concepts in JavaScript and got Introduction to it. Generally for each concept just provided a sample code in order to understand the concepts.

Here JavaScript API cheatsheet for front end developer http://overapi.com/javascript,

Image Source from http://overapi.com/javascript

So, you can get a lot more stuff and play around with each and every concept.

I had just gone through the Introduction in all concepts, so If you had any queries Just Ask in the response section.

Thanks for Reading!…

Reference From:

https://developer.mozilla.org/

https://www.phpied.com/3-ways-to-define-a-javascript-class/

React Hooks: useState, useEffect, useReducer

Learn React hooks step by step, with a look at how the same logic would be implemented with classes.

React Hooks Tutorial for Beginners: What you will learn

In the following tutorial you’ll learn:

  • how to use React hooks
  • how the same logic would be implemented in React class components

React Hooks Tutorial for Beginners: requirements

To follow along with the tutorial you should have a basic understanding of:

React Hooks Tutorial for Beginners: setting up the project

If you want to follow along with the examples make sure to configure a React development environment:

npx create-react-app exploring-hooks

(You should have one of the latest version of Node.js for running npx).

In the beginning, there was this.setState

I won’t go too deep here, I assume you’re already using React in your project but let me do a quick recap.

React is a library for building user interfaces and one of its perks is that the library itself imposes a strict data flow to the developer.

By enforcing a clear structure (container and presentational components) and a strict data flow (components react to state and props change) it's easier than before to create well-reasoned UI logic.

The basic theory in React is that a piece of UI can “react” in response to state changes. The basic form for expressing this flow was an ES6 class up until now.

Consider the following example, an ES6 class extending from, with an internal state:

import React, { Component } from "react";export default class Button extends Component {
constructor() {
super();
this.state = { buttonText: "Click me, please" };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(() => {
return { buttonText: "Thanks, been clicked!" };
});
}
render() {
const { buttonText } = this.state;
return <button onClick={this.handleClick}>{buttonText}</button>;
}
}

As you can see from the code above the component’s internal state gets mutated by this.setState when clicking the button. The text's button in turn reacts to this change and receives the updated text.

With React hooks it's possible to express the same logic without an ES6 class.

Updating the state in React … without setState

So what options do we have for managing the internal state in React now that this.setState and classes are not a need anymore?

Enter the first, and most important React hook: useState. It's a function exposed by reacting itself, you'll import it in your components as:

import React, { useState } from "react";

After importing useState you'll destructure two values out of it:

const [buttonText, setButtonText] = useState("Click me, please")

Confused by this syntax? It’s ES 2015 array destructuring.

The names above can be anything you want, it doesn’t matter to React. I advise using descriptive and meaningful variable names depending on the state’s purpose.

The argument passed to useState is the actual initial state, the data that will be subject to changes. useState returns for you two bindings:

  • the actual value for the state
  • the state updater function for said state

So the previous example, a button component, with hooks becomes:

import React, { useState } from "react";export default function Button() {
const [buttonText, setButtonText] = useState("Click me, please");
return (
<button onClick={() => setButtonText("Thanks, been clicked!")}>
{buttonText}
</button>
);
}

Let’s now take a look at data fetching with Hooks.

In the beginning, there was componentDidMount (and render props)

Data fetching in React! Do you remember the old days of componentDidMount? Here's how to fetch data from an API for rendering out a list:

import React, { Component } from "react";export default class DataLoader extends Component {
state = { data: [] };
componentDidMount() {
fetch("http://localhost:3001/links/")
.then(response => response.json())
.then(data =>
this.setState(() => {
return { data };
})
);
}
render() {
return (
<div>
<ul>
{this.state.data.map(el => (
<li key={el.id}>{el.title}</li>
))}
</ul>
</div>
);
}
}

There are a couple of shortcomings in the above code, it’s not reusable at all. With a render prop we can easily share the data with child components:

import React, { Component } from "react";export default class DataLoader extends Component {
state = { data: [] };
componentDidMount() {
fetch("http://localhost:3001/links/")
.then(response => response.json())
.then(data =>
this.setState(() => {
return { data };
})
);
}
render() {
return this.props.render(this.state.data);
}
}

Now you would consume the component by providing a render prop from the outside:

<DataLoader
render={data => {
return (
<div>
<ul>
{data.map(el => (
<li key={el.id}>{el.title}</li>
))}
</ul>
</div>
);
}}
/>

Even this pattern (born for providing a nicer alternative to mixins and HOCs) has its shortcomings.

I guess that’s the exact reason which led React engineers to come up with Hooks: provide better ergonomics for encapsulating and reusing stateful logic in React.

So impatient as I am, one of the first things I wanted to try with hooks was data fetching. What hook I’m supposed to use for fetching data? Let’s see.

Fetching data with useEffect

I thought data fetching with React hooks shouldn’t look so different from useState. A quick glance at the documentation gave me a hint: useEffect could be the right tool for the job.

I read: “useEffect serves the same purpose as componentDidMount, componentDidUpdate, and the component will unmount in React classes, but unified into a single API”

With this knowledge in hand, I refactored the first version of Dataloader to use useEffect.

The component becomes a function and fetch gets called inside useEffect.

Moreover, instead of calling this.setState I can use setData (an arbitrary function extracted from useState):

import React, { useState, useEffect } from "react";export default function DataLoader() {
const [data, setData] = useState([]);
useEffect(() => {
fetch("http://localhost:3001/links/")
.then(response => response.json())
.then(data => setData(data));
});
return (
<div>
<ul>
{data.map(el => (
<li key={el.id}>{el.title}</li>
))}
</ul>
</div>
);
}

At this point, I thought “what could be wrong?”. I launched the app. This is what I saw in the console:

It was clearly my fault because I’ve already got a hint of what was going on:

“useEffect serves the same purpose as componentDidMount, componentDidUpdate, and the component will unmount”

componentDidUpdate! it's a lifecycle method that runs every time a component gets new props, or a state change happens. That's the trick. If you call useEffect as I did you would see an infinite loop.

For fixing this “bug” you would need to pass an empty array as a second argument to useEffect:

//
useEffect(() => {
fetch("http://localhost:3001/links/")
.then(response => response.json())
.then(data => setData(data));
}, []); // << super important array
//

This array contains so-called dependencies for useEffect, that is, variables on which useEffect depends on to re-run.

When the array is empty, the effect runs only once.

Cleaning up the effect with useEffect

Timers, listeners, and persistent connections (WebSocket and friends) are the most common causes of memory leaks in JavaScript.

Consider the following use of useEffect, where we open a connection to a Socket.Io server:

useEffect(() => {    const socket = socketIOClient(ENDPOINT);
socket.on("FromAPI", data => {
setResponse(data);
});
}, []);

The problem with this code is that the connection is held open even after the component unmounts from the DOM (in response to a state change for example).

What not everybody knows about useEffect is that we can return a function to clean up the effect, that is, a function that runs when the component unmounts.

This is the equivalent of componentWillUnmount for classes. Our example becomes:

useEffect(() => {    const socket = socketIOClient(ENDPOINT);
socket.on("FromAPI", data => {
setResponse(data);
});
return () => socket.disconnect(); }, []);

Now the connection closes as expected when the component unmounts. For the full tutorial: Socket.Io with React.

Can I use render props with React hooks?

There’s no point in doing that. Our DataLoader component becomes:

import React, { useState, useEffect } from "react";export default function DataLoader(props) {
const [data, setData] = useState([]);
useEffect(() => {
fetch("http://localhost:3001/links/")
.then(response => response.json())
.then(data => setData(data));
}, []); // << super important array
return props.render(data)
}

Now you would consume the component by providing a render prop from the outside as we did in the previous example.

Again, there’s no point in doing this because Hooks are here to help share logic between components.

Let’s see an example in the next section.

Your first custom React hook

Instead of HOCs and render props, we can encapsulate our logic in a React hook and then import that hook whenever we feel the need. In our example, we can create a custom hook for fetching data.

A custom hook is a JavaScript function whose name starts with “use”, as a convention. Easier done than said. Let’s make a useFetch hook then:

// useFetch.js
import { useState, useEffect } from "react";
export default function useFetch(url) {
const [data, setData] = useState([]);
useEffect(() => {
fetch(url)
.then(response => response.json())
.then(data => setData(data));
}, []);
return data;
}

This is how you would use the custom hook:

import React from "react";
import useFetch from "./useFetch";
export default function DataLoader(props) {
const data = useFetch("http://localhost:3001/links/");
return (
<div>
<ul>
{data.map(el => (
<li key={el.id}>{el.title}</li>
))}
</ul>
</div>
);
}

This is what makes hooks so appealing: finally, we have a nice, standardized, and clean way for encapsulating and sharing logic.

NOTE: I didn’t account for fetch errors in the code above, do your homework!

Can I use async/await with useEffect?

When playing with useEffect I wanted to try async/await inside the hook. Let's see our custom hook for a moment:

// useFetch.js
import { useState, useEffect } from "react";
export default function useFetch(url) {
const [data, setData] = useState([]);
useEffect(() => {
fetch(url)
.then(response => response.json())
.then(data => setData(data));
}, []);
return data;
}

For refactoring to async/await the most natural thing you would do is probably:

// useFetch.js
import { useState, useEffect } from "react";
export default function useFetch(url) {
const [data, setData] = useState([]);
useEffect(async () => {
const response = await fetch(url);
const data = await response.json();
setData(data);
}, []);
return data;
}

Makes sense right? Then I opened the console and React was screaming at me:

“Warning: An Effect function must not return anything besides a function, which is used for clean-up.” Followed by a complete explanation of what I was doing wrong. How nice!

Turns out you cannot return a Promise from useEffect.

JavaScript async functions always return a promise and useEffect should exclusively return another function, which is used for cleaning up the effect.

That is if you were to start setInterval in useEffect you would return a function for cleaning up the interval to avoid memory leaks.

So for making React happy we could rewrite our asynchronous logic like so:

// useFetch.js
import { useState, useEffect } from "react";
export default function useFetch(url) {
const [data, setData] = useState([]);
async function getData() {
const response = await fetch(url);
const data = await response.json();
setData(data);
}
useEffect(() => {
getData();
}, []);
return data;
}

Your custom hook will work again.

Complex state changes with useReducer

Akin to useState, useReducer is another hook, convenient for dealing with more complex state changes in React components.

useReducer borrows some theory from Redux, namely the concepts of reducers, action, and dispatch.

To understand how useReducer works take a look at the following custom hook:

export function useFetch(endpoint) {
const [data, dispatch] = useReducer(apiReducer, initialState);
useEffect(() => {
dispatch({ type: "DATA_FETCH_START" });
fetch(endpoint)
.then(response => {
if (!response.ok) throw Error(response.statusText);
return response.json();
})
.then(json => {
dispatch({ type: "DATA_FETCH_SUCCESS", payload: json });
})
.catch(error => {
dispatch({ type: "DATA_FETCH_FAILURE", payload: error.message });
});
}, []);
return data;
}

Here we call the hook by passing in a reducer (you’ll see it in a moment), and an initial state:

const [data, dispatch] = useReducer(apiReducer, initialState);

In exchange, we get a state, data and a function for dispatching actions. Then to dispatch actions, which are handled by the reducer to change the state, we call dispatch in our code:

useEffect(() => {
// dispatch an action
dispatch({ type: "DATA_FETCH_START" });
fetch(endpoint)
.then(response => {
if (!response.ok) throw Error(response.statusText);
return response.json();
})
.then(json => {
// dispatch an action on success
dispatch({ type: "DATA_FETCH_SUCCESS", payload: json });
})
.catch(error => {
// dispatch an action on error
dispatch({ type: "DATA_FETCH_FAILURE", payload: error.message });
});
}, []);

These actions end up in a reducer function to calculate the next state:

const initialState = {
loading: "",
error: "",
data: []
};
function apiReducer(state, action) {
switch (action.type) {
case "DATA_FETCH_START":
return { ...state, loading: "yes" };
case "DATA_FETCH_FAILURE":
return { ...state, loading: "", error: action.payload };
case "DATA_FETCH_SUCCESS":
return { ...state, loading: "", data: action.payload };
default:
return state;
}
}

Here’s the complete example:

import { useEffect, useReducer } from "react";const initialState = {
loading: "",
error: "",
data: []
};
function apiReducer(state, action) {
switch (action.type) {
case "DATA_FETCH_START":
return { ...state, loading: "yes" };
case "DATA_FETCH_FAILURE":
return { ...state, loading: "", error: action.payload };
case "DATA_FETCH_SUCCESS":
return { ...state, loading: "", data: action.payload };
default:
return state;
}
}
export function useFetch(endpoint) {
const [data, dispatch] = useReducer(apiReducer, initialState);
useEffect(() => {
dispatch({ type: "DATA_FETCH_START" });
fetch(endpoint)
.then(response => {
if (!response.ok) throw Error(response.statusText);
return response.json();
})
.then(json => {
dispatch({ type: "DATA_FETCH_SUCCESS", payload: json });
})
.catch(error => {
dispatch({ type: "DATA_FETCH_FAILURE", payload: error.message });
});
}, []);
return data;
}

Wrapping up, and resources

React Hooks are a nice addition to the library. Born as an RFC in November 2018 they caught up quickly and landed in React 16.8.

React hooks make render props and HOCs almost obsolete and provide nicer ergonomics for sharing stateful logic.

React ships with a bunch of pre-defined hooks. The most important are useState and useEffect. useState makes it possible to use local state inside React components, without resorting to ES6 classes.

useEffect replaces componentDidMount, componentDidUpdate, and componentWillUnmount with a unified API.

For data fetching, I wouldn’t jump all in with useEffect , a lot could still change in the near future with React's concurrent mode.

Akin to useState, useReducer is another hook, convenient for managing complex state changes.

There are a lot of other hooks: I suggest reading through the official documentation for learning more.

It’s easy to foresee where React is going: functional components all over the place! Now we have 3 ways for expressing components in React:

  • functional components
  • class components
  • functional components with Hooks

There are a lot of other resources out there for learning React hooks. Here are my suggestions:

--

--

Muktarul Khan Akash
0 Followers

Frontend web developer || Web developer || JavaScript developer