Skip to content

Frequently Asked React and Front-end Interview Questions

Published:

It’s been a long time since I write a blog post. Because of my day job I was quite occupied, tired and, because of my recent job searching I wanted to write about the interview questions that I faced all the time. So, here we go.

Table of Contents

Open Table of Contents

Var vs Let vs Const

Actually, var and let is pretty similar only difference is var is function scoped and let is block scoped. It can also be said var is more like a global variable whereas let is only valid inside its parent parenthesis.

By global I mean, because of the hoisting var can be accessed from the outer scope:

if (true) {
  var a = 5;
}

console.log(a); // Prints out 5

But, if you try doing same example with let javascript will throw Uncaught ReferenceError: a is not defined

if (true) {
  let a = 5;
}

console.log(a); // Prints out Uncaught ReferenceError: a is not defined.

The main reason we use let is to avoid Hoisting which I’ll explain two minutes later in this post. Finally, const is a constant value that cannot be changed once defined. There are some exceptions which usually interviewers asks once you said you cannot change a const value once defined. Such as what if I push a variable inside const arr = [] array then what happens? It works since you don’t re-assign the value. There is one more trap question interviewer might ask what if I do this below code piece then what happens?

const obj = {};
obj.someValue = "Does it work?";

Yeah, it works. It creates key called someValue and value called Does it work?. That’s all you have to know about var, let and const.

What Is Hoisting?

Before, let and const introduced people had to use var since there were no alternatives. That caused a interesting problem called hoisting which is basically using variable before its definition. It’s easier to show then write. Come along.

// calling x after definition
var x = 5;
console.log(x, "\n");

// calling y after definition
let y = 10;
console.log(y, "\n");

// calling var z before definition will return undefined
console.log(z, "\n");
var z = 2;

// calling let a before definition will give error
console.log(a);
let a = 3;

You see, when var used javascript won’t complain and returns undefined which is pretty annoying. But, when we use let javascript complains and says you cannot call a variable before definition and throws Uncaught ReferenceError: Cannot access ‘a’ before initialization

What Is Closures?

Arghh, tough one. Interviewers loves closures, dunno why 😅. Let’s get over it. MDN document says

A closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

Closures are also like hoisting its a lot easier to show.

function init() {
  var name = "Mozilla";
  function displayName() {
    // displayName() is the inner function, a closure
    name = "Chrome";
    console.log("Inner function --> ", name); // Returns Inner function --> Chrome
  }
  console.log("Outer function --> ", name); // Returns Outer function --> Mozilla
  displayName();
}
init();

As you can see we did not change the outer value. It only changes if we call displayName() closure. That is a primitive way of emulating private values since JS doesn’t have private class fields like other languages such as C#, Java and C++. One practical use of closures may be Debounce which I’ll talk later. Finally, be careful while using this technique may lead to memory leak and performance issues.

Most Commonly Used Functions Map, Filter, Find and Reduce

Map

The map is a functional way of iterating over elements. One of the important things to know that map is an array method and returns a new array after computation finishes. Few use cases.

const arr = [🍇, 🍈 , 🍉, 🍊];
arr.map(fruit => fruit) // Returns [🍇, 🍈 , 🍉, 🍊]

const arr = [2,4,5,6];
arr.map(number => number * number) // Returns [4,16,25,36]

By the way, if you don’t need the returned array don’t use map it’s an anti-pattern use forEach instead since it does not return an array.

So, you shouldn’t be using map if:

Filter

Like map, filter is an array method that takes conditions to filter out elements.

const arr = [🍇, 🍈 , 🍉, 🍊,🍇,🍇];
arr.filter(fruit => fruit === 🍇) // Returns [🍇, 🍇 , 🍇]

//OR

arr.filter(fruit => fruit !== 🍇) // Returns [🍈 , 🍉, 🍊]

Find

The find method returns the first element that satisfies the provided condition.

const obj = [
  { name: "John", age: 18 },
  { name: "Ash", age: 19 },
  { name: "John", age: 20 },
];

obj.find(x => x.name === "John"); // Returns {name: "John", age: 18}

I intentionally left two Johns there to show that find() method returns the first element that satisfies the condition not the second one{name: 'John', age: 20}.

Reduce

reduce() is extremely versetile method. You can do pretty much everything you can do with map(). I’ll go over with some examples.

const people = [
  { id: "1", name: "John", age: 18 },
  { id: "2", name: "Smith", age: 18 },
  { id: "3", name: "Ashy", age: 12 },
];

people.reduce((acc, person) => acc + person.age, 0); // Returns 48. Total age of people.

people.reduce((acc, person) => {
  if (acc !== null) return acc;
  if (person.name === "John") return person;
  return null;
}, null);

people.reduce((acc, person) => {
  if (person.age > 25) {
    acc = [...acc, person];
  }
  return acc;
}, []);

As you can see, our first reducer returned 48, second one returned {id: "1", name: "John", age: 18} and third one returned **[{id: "1", name: "John", age: 18}, {id: "2", name: "Smith", age: 35}]**. If I have to explain how it works reduce works like a recursive function. The first parameter behaves as an accumulator basically holds our total value. So, each time reducer works it iterates over person ages and adds them to accumulator then finally returns the sum of the total.

That was how the first reducer works.The second one is pretty straight forward, its a find() method implemented in the reducer. Looks for John in peoples array when it finds John return associated object. The third reducers works similar to filter() method it actually filter() implemented in reduce. It iterates over peoples array and looks for person who’s age greater than 25.

Optional Chaining

Long before optional chaining added to Ecmascript, we had to do lots of hacky stuff to ensure data is not undefined. Things like this person && person.car && person.car.wheels if person.car is undefined and you have not used && then you are screwed, because Javascript will throw Uncaught TypeError: Cannot read property ‘name’ of undefined. This piece of code will ruin your life in runtime 😅. So, instead people came up with something like that to prevent undefined errors

const adventurer = {
  name: "Alice",
  cat: {
    name: "Dinah",
  },
};

const dogName = adventurer.dog?.name;
console.log(dogName);

Javascript will not complain about this and simply return undefined and you are good to go.

Null Coalescing Operator vs Logical Operator

First, let me tell you what || and ?? those are. The first one is Logical Or and the second one is Nullish coalescing operator. Let’s go over first one with examples.

console.log(false || "Hello"); //Returns Hello
console.log(undefined || "Hello"); //Returns Hello
console.log(null || "Hello"); //Returns Hello
console.log(0 || "Hello"); //Returns Hello

All of those will return Hello, because Logical Or first evaluates left side of the operation, if its true then returns left side if its not returns right side.

console.log(false ?? "Hello"); //Returns false
console.log(undefined ?? "Hello"); //Returns Hello
console.log(null ?? "Hello"); //Returns Hello
console.log(0 ?? "Hello"); //Returns 0

This one is a bit tricky, due to Nullish coalescing operator’s nature, if left side of the operation is either false or 0 it will return left side but if it is null or undefined then it will return Hello.

Bind vs Arrow Function

Binding

import React from "react";
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.clickHandler = this.clickHandler.bind(this);
  }

  clickHandler() {
    console.log(this);
  }

  render() {
    return <button onClick={this.clickHandler}>Click Me</button>;
  }
}

Arrow Function

import React from "react";
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
  }

  clickHandler = () => {
    console.log(this);
  };

  render() {
    return <button onClick={this.clickHandler}>Click Me</button>;
  }
}

They pretty much similar apart from their syntax. People used Binding before Arrow Functions included in ES6. So, tell your interviewer that if s/he want to avoid code smells s/he should be using arrow functions, because its way more readable and understandable.

What Is SPA

A single-page application (SPA) is a web application or website that interacts with the user by dynamically rewriting the current web page with new data from the web server, instead of the default method of the browser loading entire new pages. The goal is faster transitions that make the website feel more like a native app. Wikipedia

This is a general definition, but let me tell you how thi works in React.

ReactDOM.render(<App />, document.getElementById("root"));

If you have no idea about React, this <App /> piece of code actually stands as gateway to our React app. All the components you make will reside in <App />. And, this component will be getting rendered in <div id="root"></div> this div. So, whenever React gets rerendered you are actually still in the same page but only requested parts of the app changes. Also, if you check out the url of your browser it also stays same, because you never left the page.

SSR vs CSR

SSR stands for Server Side Rendering, which means all your code, first get prepared on the server side then send fully rendered page to the client.

Benefits of Server Side Rendering

CSR stands for Client Side Rendering. The server sends a single page without a content, then browser starts rendering components as you fetch Javascript. The downside of the CSR is; Browser shows you a blank page which impacts SEO because of First Paint (FP) and First Contentful Paint (FCP) kinda slow.

Benefits of Client Side Rendering

Higher Order Functions and Components With Examples

Higher-order functions are functions that take other functions as arguments or return functions as their results. As you can imagine, Higher-order components aka HOC also take other components as arguments and return a new altered component.

Higher Order Functions

Actually, you already knew and used higher order functions. Some of the basic higher order functions we use:

These are higher order functions built into Javascript.

Example:

const people = [
  { name: "John", age: 35, gender: "M" },
  { name: "Doe", grade: 28, gender: "M" },
  { name: "Sarah", grade: 21, gender: "F" },
];

const isMale = people => people.gender === "M";
const isFemale = people => people.gender === "F";
const getMales = people => people.filter(isMale); // Filter takes an another function as an argument.

console.log(getMales(people));

Higher Order Components

If you’ve ever implemented Authentication in React, you probably know what HOC is.

Example:

export function AuthHOC(Component) {
  return class AuthenticatedComponent extends React.Component {
    isAuthenticated() {
      return this.props.isAuthenticated;
    }

    render() {
      const loginErrorMessage = (
        <div>
          Please <a href="/login">login</a> in order to view this part of the application.
        </div>
      );

      return (
        <div>
          {this.isAuthenticated === true ? <Component {...this.props} /> : loginErrorMessage}
        </div>
      );
    }
  };
}

export default AuthHOC;

And this is how you use it:

import React from "react";
import { AuthHOC } from "./AuthenticatedComponent";

export class MyPrivateComponent extends React.Component {
  render() {
    return <div>My secret search, that is only viewable by authenticated users.</div>;
  }
}

// Now wrap MyPrivateComponent with the AuthHOC function
export default AuthHOC(MyPrivateComponent);

The primary goals of HOC:

React Lifecycle Methods

Each component in React has a lifecycle which you can manipulate through methods. First, I wanna talk about class component lifecycle methods, then I’ll explain in functional components respectively.

Class Component Lifecycles

lifecycle-methods

As you can see, there are some commonly used lifecycle methods.

Example:

import React from "react";
import ReactDOM from "react-dom";

class Header extends React.Component {
  constructor(props) {
    super(props);
    this.state = { favoritecolor: "red" };
  }
  componentDidMount() {
    setTimeout(() => {
      this.setState({ favoritecolor: "yellow" });
    }, 1000);
  }
  componentWillUnmount() {
    setTimeout(() => {
      console.log("I will unmount");
    }, 1000);
  }
  render() {
    return <h1>My Favorite Color is {this.state.favoritecolor}</h1>;
  }
}

ReactDOM.render(<Header />, document.getElementById("container"));

Functional Component Lifecycles

Thanks to React Hooks, people find a better way to utilize lifecycle methods. Some of the common methods we discussed at the top, gathered in one method, and this is useEffect(). useEffect() replaces three method.

I’ll explain this with examples:

import React, { useEffect, useState } from "react";
import "./styles.css";

export default function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log("I'm getting rendered yeyyyyy");
    FakeAPI.subscribeToFriendStatus(id, handleStatusChange);
    return () => {
      FakeAPI.unsubscribeFromFriendStatus(id, handleStatusChange);
    };
  }, [count]); // Only re-run the effect if count changes

  useEffect(() => {});

  return (
    <div className="App">
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(prevState => prevState + 1)}>Click me!</button>
    </div>
  );
}

In this example, component will first render I'm getting rendered yeyyyyy no matter what, then it’ll only render, if you click on Click me!. And, finally in return () => {} it will clean-up your listeners similar to componentWillUnmount().

How To Access DOM Directly In React

You can access DOM directly specifying the ref.

When to Use Refs

Example:

function CustomTextInput(props) {
  // textInput must be declared here so the ref can refer to it
  const textInput = useRef(null);

  function handleClick() {
    textInput.current.focus();
  }

  return (
    <div>
      <input type="text" ref={textInput} />
      <input type="button" value="Focus the text input" onClick={handleClick} />
    </div>
  );
}

useMemo and useCallback

These are broad topics that cannot be summarized in few words, but there are some key things to remember. If interviewers asks you how to eliminate unncessary render in React tell them to use useMemo() at component level and useCallback() at function level. These functions utilizes concept called memoization, which basically having a copy of old versions of objects, and using them if the data has not been changed.

For more information please, check Kent’s article about useMemo and useCallback When to useMemo and useCallback.

Context vs Redux

They both being used for global state management, and there is one important distinction, Context is built into React and can be used through useContext(). On the other hand Redux is popular statement management library favored by lots of developer. Oppose to Context, Redux might be overkill for your project depending on your needs.

Redux Flow

Redux-Flow

This beautiful GIF taken from —> Redux Data Flow.

To simply put,

Babel and Webpack

Babel allows you to use latest ES features without worrying about browser compatibility. Turns your ES6+ code into this:

// Babel Input: ES2015 arrow function
[1, 2, 3].map(n => n + 1);

// Babel Output: ES5 equivalent
[1, 2, 3].map(function (n) {
  return n + 1;
});

Webpack allows you to bundle all your assets into one or more bundles.

Async vs Promise

These are actually pretty similar concepts, but difference lies in their syntaxes. Async/Await is basically a syntactic sugar.

Promise

function promiseFunc(string) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(string);
    }, 1000);
  });
}
promiseFunc("hello").then(response => console.log(response));

Async/Await

function promiseFunc(string) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(string);
    }, 1000);
  });
}

async function doIt() {
  const myPromise = await promiseFunc("hello");
  console.log(myPromise); // "hello"
}

doIt();

Pretty much the same stuff, but Async/Await lets you write cleaner, maintainable and understandable functions.