Categories
backend database frontend javascript mongodb web development

New FULL Web Developer Course on YouTube

I decided to create a full Web Developer Course that will cover all the technologies that you need to kickstart your Full Stack Developer Journey. No bullshit, everything practical and relevant will be covered.

We will delve into following topics –

  1. HTML
  2. CSS
  3. JavaScript
  4. Node.js
  5. MongoDB

Check out the first video of this series where we cover the introduction to World Wide Web and the Evolution of the World Wide Web. The code for the video (if any) can be found on GitHub.

‚≠ź Check out the 10 JavaScript Projects in 2 Hours Video of my JavaScript Series where we build 12+ Projects using JavaScript :

Follow me on:
ūüĎČ Twitter:¬†https://twitter.com/The_Nerdy_Dev
ūüĎČ Instagram:¬†https://instagram.com/thenerdydev

Categories
frontend javascript reactjs

Create your own Exercise Planner using React.js [Complete Code]

Hey everyone ūüĎčūüŹĽ,

In this article, let us see how we can build an Exercise Planner Application using React.js.

‚úŹ Demo for the Project & Initial Setup

So the first thing you need to do is to create a new project.
Make sure to install Node.js onto your system because we will be needing that for our project.

We will be using this tool for creating a new React Project :

Create React App :

image

Install Node.js :image

Create a new React Project

// To create a new React Project 
npx create-react-app <project_name>
cd <project_name>
// To run the project 
npm start

Install React Router DOM package

npm install react-router-dom 

Install JSON-server package globally

We will be using JSON-server for our project.

npm install json-server -g

Create a new file – exercises.json

This is the file that will contain all the exercises that we will be needing in this application. This file will serve as a local JSON store for our application.

store/data.json

{
  "exercises": [
    {
      "title": "Pushups",
      "details": "Pushups are beneficial for building upper body strength. They work the triceps, pectoral muscles, and shoulders. When done with proper form, they can also strengthen the lower back and core by engaging (pulling in) the abdominal muscles",
      "complete": false,
      "id": 119
    }
  ]
}

Inside a new terminal, run the json-server by running the command

json-server -g ./src/store/data.json --watch --port=3111

Complete Code ūüĎ®ūüŹĽ‚ÄćūüíĽ

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </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();

App.jsx

import {Switch, Route } from "react-router-dom"
import HomePage from './pages/HomePage';
import './App.css';
import CreateExercise from "./pages/CreateExercise";
import Navbar from "./components/Navbar";
import EditExercise from "./pages/EditExercise";

function App() {
  return (
    <div className="App">
      <Navbar />
      <Switch>
        <Route path="/home" exact>
          <HomePage />
        </Route>
        <Route path="/create-exercise" exact>
          <CreateExercise />
        </Route>
        <Route path="/exercises/:id/edit">
          <EditExercise />
        </Route>
      </Switch>
    </div>
  );
}

export default App;


App.css

.App {
  text-align: center;
}

index.css

html {
  --pink: #D60087;
  --golden: goldenrod;
  --green: rgba(216, 235, 48, 0.83);
  --text-shadow: 2px 2px 0 rgba(0,0,0,0.2);
  font-size: 62.5%;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
  box-sizing: border-box;
}

*, *:before, *:after {
  box-sizing: inherit;
}

body {
  font-size: 2rem;
  line-height: 1.5;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' version='1.1' xmlns:xlink='http://www.w3.org/1999/xlink' xmlns:svgjs='http://svgjs.com/svgjs' width='1440' height='560' preserveAspectRatio='none' viewBox='0 0 1440 560'%3e%3cg mask='url(%26quot%3b%23SvgjsMask1000%26quot%3b)' fill='none'%3e%3crect width='1440' height='560' x='0' y='0' fill='%230e2a47'%3e%3c/rect%3e%3cpath d='M136.72 348.61a22.94 22.94 0 1 0 43.22-15.38z' stroke='%23e73635'%3e%3c/path%3e%3cpath d='M511.55 165.45L536.61 165.45L536.61 190.51L511.55 190.51z' stroke='%23037b0b'%3e%3c/path%3e%3cpath d='M483.33 455.66 a20.42 20.42 0 1 0 40.84 0 a20.42 20.42 0 1 0 -40.84 0z' fill='%23e73635'%3e%3c/path%3e%3cpath d='M1228-20.28a48.24 48.24 0 1 0-63.63 72.52z' stroke='%23037b0b'%3e%3c/path%3e%3cpath d='M1392.69 128.01 a29.96 29.96 0 1 0 59.92 0 a29.96 29.96 0 1 0 -59.92 0z' stroke='%23d3b714'%3e%3c/path%3e%3cpath d='M1076.84 554.49L1113.49 554.49L1113.49 591.14L1076.84 591.14z' stroke='%23d3b714'%3e%3c/path%3e%3cpath d='M756.26 7.46L795.56 7.46L795.56 35.82L756.26 35.82z' fill='%23d3b714'%3e%3c/path%3e%3cpath d='M25.99 370.04L26.54 370.04L26.54 423.32L25.99 423.32z' fill='%23d3b714'%3e%3c/path%3e%3cpath d='M905.99 522.28 a27.93 27.93 0 1 0 55.86 0 a27.93 27.93 0 1 0 -55.86 0z' stroke='%23d3b714'%3e%3c/path%3e%3cpath d='M878.82 241.19L928.56 241.19L928.56 290.93L878.82 290.93z' fill='%23e73635'%3e%3c/path%3e%3cpath d='M620.67 145.35L648.93 145.35L648.93 173.61L620.67 173.61z' stroke='%23037b0b'%3e%3c/path%3e%3cpath d='M941.34 228.22a0.43 0.43 0 1 0-0.84 0.22z' stroke='%23e73635'%3e%3c/path%3e%3cpath d='M142.87 85.55 a19.16 19.16 0 1 0 38.32 0 a19.16 19.16 0 1 0 -38.32 0z' stroke='%23037b0b'%3e%3c/path%3e%3cpath d='M329.59 523.81L381.54 523.81L381.54 575.76L329.59 575.76z' fill='%23e73635'%3e%3c/path%3e%3cpath d='M794.97 148.48 a17.04 17.04 0 1 0 34.08 0 a17.04 17.04 0 1 0 -34.08 0z' stroke='%23d3b714'%3e%3c/path%3e%3cpath d='M1144.93 501.44a3.73 3.73 0 1 0-6.24 4.09z' stroke='%23e73635'%3e%3c/path%3e%3cpath d='M1.85 326.79L6.52 326.79L6.52 331.46L1.85 331.46z' fill='%23d3b714'%3e%3c/path%3e%3cpath d='M1161.24 414.19L1165.88 414.19L1165.88 445.98L1161.24 445.98z' fill='%23e73635'%3e%3c/path%3e%3cpath d='M1208.54 452.49a10.52 10.52 0 1 0 10.48 18.25z' fill='%23e73635'%3e%3c/path%3e%3cpath d='M1356.2 405.97a49.06 49.06 0 1 0 59.45-78.06z' stroke='%23037b0b'%3e%3c/path%3e%3cpath d='M569.26 204.09 a47.56 47.56 0 1 0 95.12 0 a47.56 47.56 0 1 0 -95.12 0z' stroke='%23d3b714'%3e%3c/path%3e%3cpath d='M955.59 150.05a43.84 43.84 0 1 0-65.58-58.2z' stroke='%23e73635'%3e%3c/path%3e%3cpath d='M827.49 284.25L864.82 284.25L864.82 321.58L827.49 321.58z' stroke='%23d3b714'%3e%3c/path%3e%3cpath d='M718.92 539.64 a45.27 45.27 0 1 0 90.54 0 a45.27 45.27 0 1 0 -90.54 0z' fill='%23d3b714'%3e%3c/path%3e%3cpath d='M1253.87 200.13L1273.78 200.13L1273.78 220.04L1253.87 220.04z' fill='%23e73635'%3e%3c/path%3e%3cpath d='M365.3 8.63L376.7 8.63L376.7 16.04L365.3 16.04z' stroke='%23e73635'%3e%3c/path%3e%3cpath d='M691.18 292.33a16.5 16.5 0 1 0 0.63-33z' fill='%23037b0b'%3e%3c/path%3e%3cpath d='M224.16 500.66L270.06 500.66L270.06 546.56L224.16 546.56z' fill='%23e73635'%3e%3c/path%3e%3cpath d='M174.91 13.36 a25.6 25.6 0 1 0 51.2 0 a25.6 25.6 0 1 0 -51.2 0z' stroke='%23037b0b'%3e%3c/path%3e%3cpath d='M1396.28 502.2 a16.58 16.58 0 1 0 33.16 0 a16.58 16.58 0 1 0 -33.16 0z' fill='%23037b0b'%3e%3c/path%3e%3cpath d='M1337.35 217.49L1380.42 217.49L1380.42 268.22L1337.35 268.22z' fill='%23d3b714'%3e%3c/path%3e%3cpath d='M429.47 162.98a54.75 54.75 0 1 0 89.11 63.64z' fill='%23d3b714'%3e%3c/path%3e%3cpath d='M56.44 369.3L67.51 369.3L67.51 397.03L56.44 397.03z' stroke='%23e73635'%3e%3c/path%3e%3c/g%3e%3cdefs%3e%3cmask id='SvgjsMask1000'%3e%3crect width='1440' height='560' fill='white'%3e%3c/rect%3e%3c/mask%3e%3c/defs%3e%3c/svg%3e");

}
:focus {
  outline-color: var(--pink);
}

Components

components/BaseFilter.css

 .filter-nav button {
    background: none;
    border: none;
    color: #bbb;
    outline: none;
    font-size: 15px;
    text-transform: uppercase;
    margin-right: 10px;
    letter-spacing: 1px;
    font-weight: bold;
    cursor: pointer;
  }
  .filter-nav button.active {
    color: goldenrod;
  }

components/BaseFilter.jsx

import React from "react";
import './BaseFilter.css';
const BaseFilter = (props) => {

    <nav className="filter-nav">
      <button
        onClick={() => props.onUpdate('all')}
        className={props.current === 'all' ? 'active' : ''}
      >
        View all
      </button>

      <button
        onClick={() => props.onUpdate('completed')}
        className={props.current === 'completed' ? 'active' : ''}
      >
        Completed
      </button>

      <button
        onClick={() => props.onUpdate('pending')}
        className={props.current === 'pending' ? 'active' : ''}
      >
        Pending
      </button>
    </nav>
  );
};

export default BaseFilter;

components/ExerciseItem.jsx

import React from 'react'
import { Link } from 'react-router-dom';

import './ExerciseItem.css';

function ExerciseItem(props) {

  const performExerciseDeletion = () => {

    fetch(`http://localhost:3111/exercises/${props.exercise.id}`, {
      method: 'DELETE',
    })
      .then(() => props.onDeleteExercise(props.exercise.id))
      .catch((err) => console.log(err));

  };

  const performExerciseCompletion = () => {
    fetch(`http://localhost:3111/exercises/${props.exercise.id}`, {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ complete: !props.exercise.complete }),
    })
      .then(() => props.onCompleteExercise(props.exercise.id))
      .catch((err) => console.log(err));

  };
  const classes = ['exercise'];
  if (props.exercise.complete) {
    classes.push('complete');
  }
  return (
    <div className={classes.join(' ')}>
      <div className="actions">
        <h4>{props.exercise.title}</h4>
        <div className="buttons">
          <button onClick={performExerciseDeletion}>Delete</button>
          <Link to={`/exercises/${props.exercise.id}/edit`}>Edit</Link>
          <button onClick={performExerciseCompletion}>Toggle</button>
        </div>
      </div>
      <div className="details">
        <p>{props.exercise.details}</p>
      </div>
    </div>
  );
}

export default ExerciseItem;

components/ExerciseItem.css

.exercise {
  margin: 20px auto;
  background: white;
  padding: 10px 20px;
  border-radius: 10px;
  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
  border-left: 10px solid var(--pink);
}
.exercise:hover {
  box-shadow: 0 16px 32px 0 rgba(0,0,0,0.9);
}
h3 {
  cursor: pointer;
}
.actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.exercise.complete {
  border-left: 10px solid var(--green);
}
.buttons {
  display:flex;
  flex-direction: column;
}
.buttons button,a {
 color: white;
  background: var(--pink);
  padding: 0.5rem;
  border: 0;
  border: 2px solid transparent;
  text-decoration: none;
  font-weight: 600;
  font-size:2rem;
  margin-bottom: 5px;
   border-radius: 6px;

}
.buttons button:hover, a:hover, button:active, a:active {
  background: rgb(225, 127, 143);
}
h4 {
  transform: skew(-21deg);
  background: var(--golden)
}

components/ExercisesList.css

.exercises-list {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  max-width: 600px;
  margin: 0 auto;
  color: #555;
}

components/ExercisesList.jsx

import React from 'react'
import ExerciseItem from './ExerciseItem';
import './ExercisesList.css'

function ExercisesList(props) {

  if (props.exercises.length === 0) return null;

  return (
    <div className="exercises-list">
      {props.exercises.map((exercise) => (
        <ExerciseItem
          key={exercise.id}
          exercise={exercise}
          onCompleteExercise={props.onCompleteExercise}
          onDeleteExercise={props.onDeleteExercise}
        />
      ))}
    </div>
  );

}

export default ExercisesList;

components/Navbar.css

.main-nav {
    text-align: center;
    margin: 40px auto;
  }
  .main-nav a{
    display: inline-block;
    text-decoration: none;
    margin: 0 10px;
    color: goldenrod;
    font-size: 20px;

  }
  .active-style {
    border-bottom: 2px solid goldenrod;
    padding-bottom: 4px;
  }

components/Navbar.jsx

import React from 'react'
import './Navbar.css';
import { NavLink } from "react-router-dom";

function Navbar() {
  return (
     <nav className="main-nav">
      <NavLink activeClassName="active-style" to="/home">
        Home
      </NavLink>
      <NavLink activeClassName="active-style" to="/create-exercise">
        Create Exercise
      </NavLink>
     </nav>
  );
}

export default Navbar

Pages (Views)

Create a folder for pages where we will house all our page components.

pages/HomePage.jsx

import React, { useState, useEffect  } from 'react';
import BaseFilter from '../components/BaseFilter';
import ExercisesList from '../components/ExercisesList';

const HomePage = () => {
  const [exercises, setExercises] = useState([]);
  const [currentFilter, setCurrentFilter] = useState('all');

  const updateFilterHandler = (newFilter) => {
    setCurrentFilter(newFilter);
  };

  const deleteExerciseHandler = function (id) {
    const patchedExercises = exercises.filter((exercise) => exercise.id !== id);
    setExercises(patchedExercises);
  };

  const completeExerciseHandler = function (id) {
    const clonedExercises = [...exercises];
    const currentExerciseIndex = clonedExercises.findIndex(
      (exercise) => exercise.id === id
    );
    const currentExercise = clonedExercises[currentExerciseIndex];
    currentExercise.complete = !currentExercise.complete;
    setExercises(clonedExercises);
  };


  useEffect(() => {
      try {
        const response = await fetch('http://localhost:3111/exercises');
        const fetchedExercises = await response.json();
        console.log(fetchedExercises);
        setExercises(fetchedExercises);

      } catch (error) {

        console.log(error);
      }
    }

    fetchExercises();

  }, []);


  let jsx = (
    <ExercisesList
      exercises={exercises}
      onCompleteExercise={completeExerciseHandler}
      onDeleteExercise={deleteExerciseHandler}
    />
  );
  if (currentFilter === 'completed') {
    jsx = (
      <ExercisesList
        exercises={exercises.filter((exercise) => exercise.complete)}
        onCompleteExercise={completeExerciseHandler}
        onDeleteExercise={deleteExerciseHandler}
      />
    );
  } else if (currentFilter === 'pending') {
    jsx = (
      <ExercisesList
        exercises={exercises.filter((exercise) => !exercise.complete)}
        onCompleteExercise={completeExerciseHandler}
        onDeleteExercise={deleteExerciseHandler}
      />
    );
  }
  return (
    <div>
      <BaseFilter 
        onUpdate={updateFilterHandler} 
        current={currentFilter} />
      {jsx}
    </div>
  );
};

export default HomePage;

pages/CreateExercise.css

form  {
    padding: 20px;
    border-radius: 10px;
    text-align: center;
  }
  label {
    display: block;
    color: goldenrod;
    text-transform: uppercase;
    font-size: 14px;
    font-weight: bold;
    letter-spacing: 1px;
    margin: 20px 0 10px 0
  }
  input {
    padding: 10px;
    width: 400px;
    max-width: 100%;
    box-sizing: border-box;
    border: 1px solid var(--grey);
  }
  textarea {
    padding: 10px;
    max-width: 100%;
    width: 400px;
    box-sizing: border-box;
    border: 1px solid var(--grey);

  }
  form button {
    display: block;
    margin: 20px auto;
    background: goldenrod;
    color: white;
    padding: 10px;
    border-radius: 6px;
    font-size: 16px;
    border: 2px solid transparent;
  }

pages/CreateExercise.jsx

import React, { useState } from 'react';
import './CreateExercise.css';
import { useHistory } from 'react-router-dom';

const CreateExercise = () => {

  const [exercise, setExercise] = useState({
    title: '',
    details: ''
  })

  const history = useHistory();

  const handleChange = event => {
    setExercise({
      ...exercise,
      [event.target.name] : event.target.value
    });
  }

  const handleExerciseCreation = (event) => {

    event.preventDefault();

    const newExercise = {
      title: exercise.title,
      details : exercise.details,
      complete: false,
      id: Math.floor(Math.random() * 10000),
    };

    console.log(newExercise);

    fetch('http://localhost:3111/exercises', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(newExercise)
    }).then(() => {
      history.push('/home');
    }).catch(err => console.log(err))
  };

  return (
    <form  onSubmit={handleExerciseCreation}>
      <label>Title</label>
      <input type="text" onChange={handleChange} name="title" value={exercise.title} maxLength="15" required />
      <label>Details</label>
      <textarea value={exercise.details} name="details"  onChange={handleChange} required></textarea>
      <button>Add Exercise</button>
    </form>
  );
};

export default CreateExercise;

pages/EditExercise.jsx

import React, { useState, useEffect } from 'react';
import './CreateExercise.css';
import { useHistory, useParams } from 'react-router-dom';

const EditExercise = () => {

  const [exercise, setExercise] = useState({
    title: '',
    details: '',
  });

  const params = useParams();
  const exerciseId = params.id;
  const history = useHistory();

  const handleChange = (event) => {
    setExercise({
      ...exercise,
      [event.target.name]: event.target.value,
    });
  };

  useEffect(() => {

    fetch(`http://localhost:3111/exercises/${exerciseId}`)
      .then((res) => res.json())
      .then((data) => {
         setExercise({
           title: data.title,
           details: data.details,
         });
      })
      .catch((err) => console.log(err));

  }, [exerciseId])

  const handleExerciseUpdation = (event) => {

     event.preventDefault();
     fetch(`http://localhost:3111/exercises/${exerciseId}`, {
       method: 'PATCH',
       headers: { 'Content-Type': 'application/json' },
       body: JSON.stringify(exercise),
     })
       .then(() => {
         history.push('/home');
       }).catch((err) => console.log(err));
  };

  return (
    <form onSubmit={handleExerciseUpdation}>
      <label>Title</label>
      <input
        type="text"
        onChange={handleChange}
        name="title"
        value={exercise.title}
        maxLength="15"
        required
      />
      <label>Details</label>
      <textarea
        value={exercise.details}
        name="details"
        onChange={handleChange}
        required
      ></textarea>
      <button>Update Exercise</button>
    </form>
  );
};

export default EditExercise;

So this is the code for our entire project.

PS – If you prefer learning React with a video. I do have a video on same where we create this exact project.

Check this :

Thanks for reading !

Categories
javascript

Arrow Functions vs Normal Functions

Hey everyone ūüĎčūüŹĽ,
In this article, let us understand the differences between Arrow Functions and Normal Functions.

Arrow Functions vs Normal Functions

We know that we can define JavaScript functions in many ways:

  • The first way is by making use of the function keyword.

So as an example, consider this piece of code :

function greet(name) { 
  return `Hey ${name}`;
}

This comes under normal functions that we declare when we write code. Another syntax that we often see getting used in the code is Function Expression Syntax

So let us transform the above function declaration into a function expression :

const greet = function(name) { 
  return `Hey ${name}`; 
}
  • The second way is using the arrow function syntax :

So let us transform the above code based on function expression to a code that uses the arrow function syntax

const greet = name => {
  return `Hey ${name}`;
}

Now if you see any of the above syntaxes can be used for defining functions, what you choose depends on you but knowing the differences between these syntaxes will make it easier to decide regarding the choice of syntax in different cases.

 this keyword

this keyword as we know is a property of an execution context. The main use of it is observed in functions and constructors. Here is a video where you can learn about the rules of the this keyword :

https://youtu.be/O5aShSPaMbE

  • Normal functions

this inside a normal function is dynamic. By dynamic, it means that the value of this depends on how we invoke the function. Let us see the ways we can invoke a normal function.
So during a normal invocation, the value of this equals to the global object. There is a small exception to this : If the function runs in strict mode, then the this keyword equals undefined. So let us see an example :

function someFunction() { 
   console.log(this);
}
someFunction(); 

The above examples logs the window object to the console.

Next, if we have a method that is sitting within an object, then the this keyword when referenced in the scope of method will point to the object that owns that particular methods which also represents the context in which that method is defined in.

So let us see an example for this as well :

const someObject =  { 
   greet() { 
        console.log(this);
        console.log('hello');
   }
};
someObject.greet(); 

So the above code calls the greet method that is sitting inside the someObject object.

Next we can also make use of the call method to perform an indirect invocation, here the value of this equals to the first argument that the call method receives.

function someFunction() { 
   console.log(this);
}
const someContext = { a : 'some value' };
someFunction.call(someContext); 

For the above example code, the this keyword points to the someContext object.

Next, let us see what this equals to in a constructor function that is created using the normal function syntax :

function User() { 
   console.log(this);
}
new User(); 

If we invoke the constructor function using the new keyword, then this equals to the newly created instance.

  • Arrow Functions

Arrow functions do not have their own this, instead they look up the scope chain to find a this that they can use (or until the lookup hits the global scope) much in the same way as a variable resolves its value. So if you reference a variable in a nested scope, then the lookup will go up the scope chain until it resolves its declaration. This in essence means that arrow function does not define its own execution context. This also means that no matter how or where the arrow functions get executed, the this value inside an arrow function always equals the this value from the outer function. IOW, arrow functions resolve the this in a lexical manner.

Let us take an example :

const someObject = { 
   someMethod(elements) { 
     console.log(this); 
     elements.forEach(element => { 
        console.log(element);
        console.log(this);
     });
   }
};
someObject.someMethod([1,2,3,4,5]);

this value inside the callback of forEach equals to this of the outer function someMethod

Constructors

  • Normal Functions We now know that normal functions can be used for creating new objects. So for example, if we want to create a new user, then we can create a constructor function with the name User and that will give us the ability to create new instances of a car when used with the new keyword. So let us see the code for this :
function User(name, age, profession) {  
  this.name = name; 
  this.age = age; 
  this.profession = profession;
}
const newUser = new User('alex', 21, 'developer');
console.log(newUser instanceof User); 

So if you see above, User is a normal function. When invoked with the new keyword, new User('alex', 21, 'developer') – new instances of User gets created.

  • Arrow Functions

Arrow functions cannot be used as constructor. So if you try to invoke an arrow function using the new keyword, JavaScript will yell at you with an error. So let us see an example for this as well :

const User = () => { 
   this.name = name; 
   this.age = age;
   this.profession = profession; 
}
const newUser = new User('alex',21, 'developer'); 

So for above code, invoking new User('alex', 21, 'developer') will throw an error, specifically this error :

TypeError: User is not a constructor

– Implicit Return

  • Normal Function

See the following code :

function someFunction() { 
  return 'some value';
} 
const value = someFunction(); 
console.log(value);

In the above code, we are just invoking the normal function someFunction and at the point of invocation, we receive a value which we store in value variable that we defined above. Now if we omit the return statement in the above code, or if there is no expression after the return statement, the normal function will do an implicit return here with the value of undefined. So let me demonstrate this with the help of an example :

function someFunction() { 
  'some value';
}
function someFunction2() { 
 return;
}
console.log(someFunction());
console.log(someFunction2()); 

In above code, the first console.log will give us undefined because it misses the return keyword, it actually returns nothing. Coming to the second console.log, it does return but there is no value as such, hence this is also undefined

  • Arrow Function

In case of an arrow function, you can perform implicit return but the condition for same is that your arrow function should contain only one expression. If so is the case, you can then omit the function’s curly braces, and simply do an implicit return for the expression. So let us see an example for this :

const multiplyBy2 = num => num * 2; 
console.log(multiplyBy2(3)); 

The multiplyBy2 arrow function if you see comprises of just one expression num => num * 2. So here the expression is implicitly returned by the arrow function
without the use of return keyword.

Methods

  • Normal Function

Normal functions as we know is the usual way for defining methods on classes.

Let us see an example :

class User {
   constructor(name, age, profession) {
       this.name = name; 
       this.age = age; 
       this.profession = profession; 
   }
   logUserDetails() { 
      console.log(this.name);
      console.log(this.age);
      console.log(this.profession); 
   } 
 } 
const user = new User('alex', 21, 'developer'); 
user.logUserDetails();  

So the above code does works. But let us assume that we need to supply the logUserDetails method as a callback, say for example to a setTimeout or some other event listener. Now in such cases, we may get a wrong context while trying to access the this value.
So consider this code :

setTimeout(user.logUserDetails, 1000);

So after 1 second, the above code will call the logUserDetails method on the user object. Now for the above code this points to the window object.

Let us manually fix the context so by correctly binding this value to point to the user object. So for this , we can make use of the bind method.

setTimeout(user.logUserDetails.bind(user), 1000);

In the above code, user.logUserDetails.bind(user) binds the this value to the user instance. So this ensures that we don’t loose the context and that the this keyword is correctly bound to the instance.

There is another workaround for this as well which is using arrow functions.

  • Arrow Function

Now with the introduction of class fields, we can use the arrow functions as methods inside classes.

Contrary to the normal functions, the method that we define using arrow functions binds this lexically to the instance of the class.

So let us see an example for same :

class User {
   constructor(name, age, profession) {
       this.name = name; 
       this.age = age; 
       this.profession = profession; 
   }
   logUserDetails = () => { 
      console.log(this.name);
      console.log(this.age);
      console.log(this.profession); 
   } 
 } 
const user = new User('alex', 21, 'developer'); 
user.logUserDetails();  

Now for the above code, if we use user.logUserDetails as a callback without even performing a bind, it will give us the correct values. The value of this inside our logUserDetails will be the class instance itself.
So if you try the below code, you will get alex, 21 and developer to the console as output.

setTimeout(user.logUserDetails, 1000);

So this is it for this article. Thanks for reading.

If you enjoy my articles, consider following me on Twitter for more interesting stuff :

Image description

‚ö°Twitter : https://twitter.com/The_Nerdy_Dev

Don’t forget to leave a like if you loved the article. Also share it with your friends and colleagues.

Categories
javascript Uncategorized

Arrow Functions – When and When not to use them ?

Hey everyone ūüĎčūüŹĽ,
In this article, let us understand When and when not should we use arrow functions.

Arrow Functions

With ES6, JavaScript has evolved a lot because of the customizations that the new standard brings to the language. It has immensely improved our development experienced with the introduction of Next Generation JavaScript features. One of such features that ES6 brings to the table is the Arrow Functions.
The purpose of writing this article is to make the reader aware about the scenarios where he/she should not use arrow functions and instead make use of the good old school function syntax or the function expression syntax. We need to be a bit careful as well while using arrow functions because they can affect the readability of our code as well.

When to use Arrow Functions ?

There are some solid reasons why using arrow functions can be a plus.

  1. Securing the scope : The arrow functions remember their context in which they are defined in and they stick to that.
    So even if we introduce a normal callback function and mix that with a couple of arrow functions, then that can lead to messing up of the scope.
  2. Compact : Easier to read and write. Just a fat arrow, and they can be anonymous as well.
  3. Code Clarity: When every function in your code is an arrow function, then a normal function is the one that we rely on for defining the scope. So we as developers can always look up the next higher order function statement to figure out the this keyword.

When not to use arrow functions ?

1. Defining methods on an object

In JavaScript, as we know a method is a function that we set in a property of the object. So right at the time of invoking the method, the this keyword points to the object that the method belongs to.

Now let us talk about Object Literals here. Let us say we want to define a method inside an object literal. At first, you might be tempted to use an arrow function for a method definition within the object literal. But let us understand with the help of an example, why you should not use an arrow function within the object literals.

const user = { 
  stocks : [
     {
      stock : 'Amazon', 
      price_in_dollars : 937,
      units_bought: 5
   },
   {
      stock : 'Tesla', 
      price_in_dollars : 652,
      units_bought: 3
   }, 
   {
      stock : 'Meta Platforms', 
      price_in_dollars : 465,
      units_bought: 2
   }
],
get_total_invested : () => { 
        console.log(this === window);
        return this.stocks.reduce((acc, stock) => acc + stock.price_in_dollars * stock.units_bought, 0);
  }
};

console.log(this === window);
const invested_amount = user.get_total_invested(); 
console.log(invested_amount); 

user.get_total_invested method as we can see is defined within an arrow function. But if you try the above code, the invocation to user.get_total_invested would throw a TypeError because in the above case this.stocks would resolved to undefined.

Image description

Now the thing to note here is that when we are invoking the get_total_invested method on the user object, the context in which it is called in is the window object which we can easily verify by comparing this keyword with the window object in the get_total_invested method as you can see within this method, the this keyword points to the window object. So in essence, this.stocks resolves to window.stocks and this ultimately gives us undefined because we don’t have any stocks property on the window object.

In order to solve this, we can simply switch to the function expression syntax or its shorthand for method definition. For this case, at the invocation point, this keyword would refer to the the thing to the left of the dot at the invocation point. Here is the code with the above modifications :

const user = { 
  stocks : [
     {
      stock : 'Amazon', 
      price_in_dollars : 937,
      units_bought: 5
   },
   {
      stock : 'Tesla', 
      price_in_dollars : 652,
      units_bought: 3
   }, 
   {
      stock : 'Meta Platforms', 
      price_in_dollars : 465,
      units_bought: 2
   }
],
get_total_invested(){ 
        console.log(this === user);
        return this.stocks.reduce((acc, stock) => acc + stock.price_in_dollars * stock.units_bought, 0);
  }
};

const invested_amount = user.get_total_invested(); 
console.log(invested_amount);

Now in the above code, get_total_invested is a method that is being called on the user object. So with the regular function syntax, the thing to the left of dot which is the user object here is what this resolves to in the end and therefore the above code would work fine. Here is the output for the above code :

Image description

From the output, you can also easily decipher that the this keyword indeed points to the user object and hence this === user returns true as a result.

Let us now move forward and take arrow functions with Object Prototype

function User(name, age, profession) { 
  this.name = name; 
  this.age = age; 
  this.profession = profession;
}
User.prototype.sayHello = () => { 
  console.log(this === window);
  return `Hello ${this.name}`; 
}
const user = new User('Alex',24,'Developer');
console.log(user.sayHello());

Now here also the same thing holds true as discussed above. In the above code, instead of using the arrow function syntax for defining our sayHello method where the this keyword points to window we should instead use the function expression syntax to solve this. So here is the modified code :

function User(name, age, profession) { 
  this.name = name; 
  this.age = age; 
  this.profession = profession;
}
User.prototype.sayHello = function(){ 
  console.log(this === window);
  return `Hello ${this.name}`; 
}
const user = new User('Alex',24,'Developer');
console.log(user.sayHello());

So with just this simple change, sayHello regular function is changing its context to user object when called as a method : user.sayHello().

2. Arrow Functions cannot be used as constructors

For constructors, use regular functions otherwise JavaScript will throw an error if it encounters an arrow function getting used as a constructor. This means that JavaScript implicitly prevents from doing that by throwing an exception.

Let us go a bit deeper inside and try to understand a bit more on this :

In simple words, the objects (or the “constructors”) rely on the this keyword to be able to be able to be modified.

The this keyword always references the global object which is the window object.

So if we have something like this :

const User = (name, age, profession) => { 
    this.name = name; 
    this.age = age;
    this.profession = profession; 
}
const user = new User("Alex", 21, "developer"); 

Executing new User("Alex", 21, "developer"), where User is an arrow function, JavaScript throws a TypeError that User cannot be used as a constructor.

But, instead if you do something like this :

const User = function(name, age, profession) {
   this.name = name;
   this.age = age;
   this.profession = profession; 
}
const user = new User('Alex',21,'developer');
console.log(user.name);

Now here, the new keyword does some magic under the hood and makes the this keyword that is inside the User to initially first refer to the empty object instead of referencing to the global object like it earlier did. Right after this, new properties like name,age and gender get created inside this empty object pointed to by the this keyword and the values will be Alex,21 and developer respectively. Finally, the object pointed to by the this keyword gets returned.

Now a thing to note here is that arrow functions do not allow their this object to be modified. Their this object is always the one from the scope where they were statically created. This is called Static Lexical Scope. This is precisely the reason that why we cannot do operations like bind, apply, or call with arrow functions. Simply, their this is locked to the value of the this of the scope where they were created.

3. Arrow Functions as callback functions

As we know in client side JavaScript, attaching event listeners to the DOM elements is a very common task. An event triggers the handler function with this as the target element.

So for example, let us say we have this code 

const button = document.querySelector('button');
button.addEventListener('click',() => { 
 console.log(this === window); 
 this.innerHTML = 'Changed';
});

Now as discussed just a while back, this points to the window object in an arrow function that is defined in the global context. So whenever a click event occurs, browser tries to execute the handler function with the button as context, but arrow functions still stays bound to its pre-defined context.
In above code, this.innerHTML is same as window.innerHTML which does nothing infact.

In order to solve this, we need to make use of the function expression syntax which will allow us to change this depending on the target element.

const button = document.querySelector('button');
button.addEventListener('click', function() { 
 console.log(this === button); 
 this.innerHTML = 'Changed';
});

Now for the above code, this keyword will infact point to the button. Therefore, this.innerHTML will correctly modify the button text and reflect the same in the UI.

So this is it for this article. Thanks for reading.