Home / ReactJS high-level interview question and answer

ReactJS high-level interview question and answer

What is HOC(Higher order component)?

A Higher Order Component (HOC) in React is a pattern where a function takes a component and returns a new component with enhanced functionality. It’s a way to reuse component logic.

Example:

import React from 'react';

// Higher Order Component function
const withUpperCase = (WrappedComponent) => {
  // Return a functional component
  return function WithUpperCase(props) {
    const { text } = props;
    const upperCaseText = text.toUpperCase();

    // Render the WrappedComponent with enhanced props
    return <WrappedComponent {...props} upperCaseText={upperCaseText} />;
  };
};

// Example function component to be enhanced
const DisplayText = ({ text, upperCaseText }) => {
  return (
    <div>
      <p>Original Text: {text}</p>
      <p>Uppercase Text: {upperCaseText}</p>
    </div>
  );
};

// Enhance DisplayText with withUpperCase HOC
const DisplayTextWithUpperCase = withUpperCase(DisplayText);

// Usage example
const App = () => {
  return (
    <div>
      <DisplayTextWithUpperCase text="Hello, World!" />
    </div>
  );
};

export default App;
In this example:

withUpperCase is a Higher Order Component that takes a function component (DisplayText) as an argument and returns a new function component (WithUpperCase).
WithUpperCase function component computes upperCaseText by converting text prop to uppercase and then renders the WrappedComponent (DisplayText) with this enhanced prop.
DisplayText is a simple function component that displays both the original and uppercase text.
DisplayTextWithUpperCase is the enhanced component created by applying the withUpperCase HOC to DisplayText.
App component demonstrates the usage of DisplayTextWithUpperCase.
This example showcases how you can create a Higher Order Component for a function component using a functional approach with hooks like useState or useEffect as needed.

Higher Order Components allow for separation of concerns and reusability of component logic across different parts of your application.

Concepts of shallow copy and deep copy with simple examples in JavaScript.

Shallow Copy:

A shallow copy means constructing a new collection object and then populating it with references to the child objects found in the original. In essence, it creates a new object, but the nested objects within it are still referenced from the original object. Shallow copies duplicate as little as possible.

Example of Shallow Copy:

// Original object
const originalObject = {
  name: 'John',
  age: 30,
  address: {
    city: 'New York',
    country: 'USA'
  }
};

// Creating a shallow copy using Object.assign() or spread operator
const shallowCopy = { ...originalObject };

// Modifying the shallow copy
shallowCopy.name = 'Alice';
shallowCopy.address.city = 'San Francisco'; // This will affect the originalObject's address as well

console.log(originalObject);
console.log(shallowCopy);
In the example above:

originalObject has a nested address object.
shallowCopy is created using the spread operator (...) to shallowly copy originalObject.
Modifying shallowCopy.name affects only shallowCopy, but modifying shallowCopy.address.city also affects originalObject because the address object is shared (not deeply copied).

Deep Copy:

A deep copy, on the other hand, creates a new collection object and recursively adds copies of nested child objects found in the original. It means duplicating every level of nested objects, so changes in the copy do not affect the original.

Example of Deep Copy:

// Original object
const originalObject = {
  name: 'John',
  age: 30,
  address: {
    city: 'New York',
    country: 'USA'
  }
};

// Creating a deep copy using JSON methods (stringify and parse)
const deepCopy = JSON.parse(JSON.stringify(originalObject));

// Modifying the deep copy
deepCopy.name = 'Alice';
deepCopy.address.city = 'San Francisco'; // This will NOT affect the originalObject's address

console.log(originalObject);
console.log(deepCopy);
In this example:

deepCopy is created by first converting originalObject to a JSON string (JSON.stringify(originalObject)), then parsing it back into an object (JSON.parse(...)). This ensures that all nested objects are deeply copied.
Modifying deepCopy.name or deepCopy.address.city does not affect originalObject because all nested objects are separate instances.

Key Differences:

  • Shallow Copy: Only the immediate properties of the object are duplicated, including references to nested objects. Changes to nested objects in the shallow copy affect the original object.
  • Deep Copy: All properties and nested objects are duplicated recursively, ensuring changes to the copy do not affect the original object.

How to pass data child to parent component in react?

In React, passing data from a child component to a parent component involves lifting state up from the child to the parent. This is necessary because React’s data flow is typically unidirectional (from parent to child). Here’s how you can achieve this:

Method 1: Callbacks as Props

The most common way to pass data from child to parent in React is by passing a callback function as a prop from the parent to the child. The child component can then call this callback function and pass data back to the parent.

Parent Component:

import React, { useState } from 'react';
import ChildComponent from './ChildComponent';

const ParentComponent = () => {
  const [dataFromChild, setDataFromChild] = useState('');

  // Callback function to receive data from child
  const handleDataFromChild = (data) => {
    setDataFromChild(data);
  };

  return (
    <div>
      <h2>Parent Component</h2>
      <p>Data from Child: {dataFromChild}</p>
      <ChildComponent sendDataToParent={handleDataFromChild} />
    </div>
  );
};

export default ParentComponent;

Child Component (ChildComponent):

import React from 'react';

const ChildComponent = ({ sendDataToParent }) => {
  const handleClick = () => {
    // Simulating data passed from child to parent
    const data = 'Data sent from child component';
    sendDataToParent(data); // Call the callback function from props
  };

  return (
    <div>
      <h3>Child Component</h3>
      <button onClick={handleClick}>Send Data to Parent</button>
    </div>
  );
};

export default ChildComponent;

Explanation:

  1. Parent Component:

    • Defines a state variable dataFromChild to store data received from the child.
    • Declares a callback function handleDataFromChild that updates dataFromChild with the received data.
    • Passes handleDataFromChild as a prop (sendDataToParent) to the ChildComponent.
  2. Child Component:

    • Receives sendDataToParent as a prop from the parent.
    • Defines an event handler (handleClick) that triggers when a button is clicked (or any other event occurs).
    • Inside handleClick, it calls sendDataToParent with the data you want to pass back to the parent.

Alternative Methods:

  • Context API: For passing data through multiple layers of components without manually passing props down through each level.
  • State Management Libraries (like Redux): For managing global state and sharing data between components without relying on direct parent-child relationships.

What is Controlled and uncontrolled components in react with example?

Controlled Components

Controlled components are React components that manage their state internally and update it based on user input. They receive their current value through props and notify changes through callbacks like onChange.

Example:

import React, { useState } from 'react';

function ControlledInput() {
  const [value, setValue] = useState('');

  const handleChange = (event) => {
    setValue(event.target.value);
  };

  return (
    <input
      type="text"
      value={value}
      onChange={handleChange}
    />
  );
}

export default ControlledInput;

Explanation:

  • The ControlledInput component uses the useState hook to maintain the value state internally.
  • The <input> element is controlled because its value is determined by the value state variable.
  • handleChange function updates the value state whenever the input changes, ensuring that React manages and reflects the current state of the input field.

Uncontrolled Components

Uncontrolled components in React rely on the DOM to manage and store their state. They often use refs to directly access the DOM elements to retrieve their current values.

Example:

import React, { useRef } from 'react';

function UncontrolledInput() {
  const inputRef = useRef(null);

  const handleSubmit = (event) => {
    event.preventDefault();
    console.log(inputRef.current.value); // Accessing input value directly
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        ref={inputRef}
      />
      <button type="submit">Submit</button>
    </form>
  );
}

export default UncontrolledInput;
  • Explanation:
    • The UncontrolledInput component uses a ref (inputRef) to directly access the DOM node of the input element.
    • When the form is submitted, handleSubmit function logs the current value of the input directly from the DOM using inputRef.current.value.
    • React does not manage the state of the input field; it’s managed by the DOM itself, making it an uncontrolled component.

Key Differences:

  • State Management: Controlled components manage state within React components using state variables (like useState).
  • DOM Interaction: Uncontrolled components rely on direct DOM manipulation and typically use refs to interact with DOM nodes.
  • Event Handling: Controlled components use React’s synthetic events (onChange, onClick) to handle user interactions. Uncontrolled components may use traditional DOM events (onSubmit, onClick) and directly access DOM properties like value.

Write a simple React program that fetches data from an API, displays it in a table, and allows users to delete individual records by clicking a delete button next to each row.

import React, { useState, useEffect } from 'react';

const DataTable = () => {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetchData();
  }, []);

  const fetchData = async () => {
    try {
      const response = await fetch('https://jsonplaceholder.typicode.com/users');
      if (!response.ok) {
        throw new Error('Network response was not ok');
      }
      const json = await response.json();
      setData(json);
    } catch (error) {
      console.error('Error fetching data: ', error);
    }
  };

  const handleDelete = (id) => {
    const updatedData = data.filter((item) => item.id !== id);
    setData(updatedData);
  };

  return (
    <div>
      <h2>Users</h2>
      <table>
        <thead>
          <tr>
            <th>Name</th>
            <th>Email</th>
            <th>Company</th>
            <th>Action</th>
          </tr>
        </thead>
        <tbody>
          {data.map((user) => (
            <tr key={user.id}>
              <td>{user.name}</td>
              <td>{user.email}</td>
              <td>{user.company.name}</td>
              <td>
                <button onClick={() => handleDelete(user.id)}>Delete</button>
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
};

export default DataTable;

Explanation:

  • DataTable Component:
    • useState: data state holds the fetched user data from the API.
    • useEffect: Fetches data from https://jsonplaceholder.typicode.com/users when the component mounts.
    • fetchData: Fetches data from the API and updates the data state.
    • handleDelete: Removes a user from the data state array when the delete button is clicked.
    • Render: Maps over the data array to display each user in a table row. Each row has a delete button that calls handleDelete with the user’s id.
  • App Component:
    • Imports and renders the DataTable component, which encapsulates the table and delete functionality.

Notes:

  • This example uses fetch for making HTTP requests. You can replace the URL in fetchData with your own API endpoint.
  • Error handling for the fetch operation and other edge cases (like empty responses) should ideally be added for production-ready applications.
  • Ensure you handle state updates and API interactions according to your application’s requirements and best practices.