DailyDevDiet

logo - dailydevdiet

Learn. Build. Innovate. Elevate your coding skills with dailydevdiet!

Chapter 3: Basic JavaScript for React

Basic JavaScript for React

React is built on JavaScript, and having a solid understanding of modern JavaScript features is essential for effective React development. This chapter will cover the basic JavaScript for React concepts and features that are commonly used in React applications.

ES6+ Features Essential for React

Modern React code relies heavily on ECMAScript 2015 (ES6) and later features. Let’s explore the most important ones:

1. let and const

let and const are block-scoped variable declarations that replace var in modern JavaScript.

// let allows reassignment
let count = 0;
count = 1; // Works fine

// const prevents reassignment
const API_URL = 'https://api.example.com';
// API_URL = 'https://new-api.example.com'; // Error: Assignment to constant variable

In React, you’ll typically use:

  • const for imports, component definitions, and values that shouldn’t change
  • let for variables that need to be reassigned (less common in React components)

2. Arrow Functions

Arrow functions provide a concise syntax for writing functions and maintain the parent scope’s this value.

// Traditional function
function add(a, b) {
  return a + b;
}

// Arrow function
const add = (a, b) => a + b;

// Arrow function with multiple lines
const multiply = (a, b) => {
  const result = a * b;
  return result;
};

In React, arrow functions are commonly used for:

  • Component definitions (functional components)
  • Event handlers
  • Callback functions
  • Array methods like map, filter, and reduce

3. Template Literals

Template literals allow for string interpolation and multi-line strings.

const name = 'React Developer';
const greeting = `Hello, ${name}!`; // String interpolation

const multiLine = `This is
a multi-line
string`;

In React, template literals are useful for:

  • Dynamic class names
  • Constructing URLs
  • Creating complex JSX strings

4. Object and Array Destructuring

Destructuring allows you to extract values from objects and arrays concisely.

// Object destructuring
const user = { name: 'John', age: 30 };
const { name, age } = user;
// name = 'John', age = 30

// Array destructuring
const colors = ['red', 'green', 'blue'];
const [primary, secondary] = colors;
// primary = 'red', secondary = 'green'

// Default values
const { country = 'USA' } = user;
// country = 'USA' (default value used since it wasn't in the object)

In React, destructuring is extensively used for:

  • Extracting props in functional components
  • Accessing state and dispatchers from hooks
  • Working with API responses

5. Spread and Rest Operators

The spread (…) operator allows for expanding arrays and objects, while the rest parameter syntax collects multiple elements.

// Spread operator with arrays
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]

// Spread operator with objects
const baseObj = { a: 1, b: 2 };
const newObj = { ...baseObj, c: 3 }; // { a: 1, b: 2, c: 3 }

// Rest parameter in functions
function sum(...numbers) {
  return numbers.reduce((total, num) => total + num, 0);
}
sum(1, 2, 3, 4); // 10

In React, these operators are crucial for:

  • Creating new state objects without mutating the original (immutability)
  • Merging props
  • Collecting remaining props with …rest
  • Creating copies of arrays in state updates

6. Enhanced Object Literals

ES6 introduced shorter syntax for object method definitions and property names.

const name = 'John';
const age = 30;

// Enhanced object literals
const person = {
  name, // Same as name: name
  age,  // Same as age: age
  // Method shorthand
  greet() {
    return `Hello, I'm ${this.name}`;
  }
};

7. Default Parameters

Default parameters allow function parameters to have default values if no value is passed.

function greet(name = 'Guest') {
  return `Hello, ${name}!`;
}

greet(); // "Hello, Guest!"
greet('John'); // "Hello, John!"

In React, default parameters are often used for:

  • Setting default prop values for components
  • Handling optional parameters in utility functions

8. Classes

ES6 classes provide a clearer syntax for object-oriented programming.

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    return `Hello, I'm ${this.name}`;
  }
}

const person = new Person('John', 30);
console.log(person.greet()); // "Hello, I'm John"

While class components are being replaced by functional components in modern React, understanding classes is still important for:

  • Legacy React code
  • Extending error boundaries
  • Understanding lifecycle methods

9. Modules (import/export)

ES6 modules allow you to organize code into separate files.

// utils.js
export const add = (a, b) => a + b;
export const multiply = (a, b) => a * b;
export default function divide(a, b) { return a / b; }

// main.js
import divide, { add, multiply } from './utils';

In React applications, modules are essential for:

  • Organizing components
  • Importing libraries
  • Sharing utility functions
  • Managing project structure

10. Promises and Async/Await

Promises and async/await syntax provide cleaner ways to handle asynchronous operations.

// Using Promises
fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

// Using async/await
async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Error:', error);
  }
}

In React, these are crucial for:

  • API calls in useEffect hooks
  • Data fetching
  • Event handlers that perform asynchronous operations

JavaScript Array Methods for React

React often works with lists of data, making these array methods particularly valuable:

1. map()

Transforms each element of an array and returns a new array. Essential for rendering lists in React.

const numbers = [1, 2, 3, 4];
const doubled = numbers.map(num => num * 2); // [2, 4, 6, 8]

// In React:
const listItems = users.map(user => (
  <li key={user.id}>{user.name}</li>
));

2. filter()

Creates a new array with elements that pass a test.

const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter(num => num % 2 === 0); // [2, 4]

// In React:
const activeUsers = users.filter(user => user.isActive);

3. reduce()

Executes a reducer function on each element, resulting in a single output value.

const numbers = [1, 2, 3, 4];
const sum = numbers.reduce((total, num) => total + num, 0); // 10

// In React - calculating totals from an array of objects:
const totalPrice = items.reduce((total, item) => total + item.price, 0);

4. find()

Returns the first element that passes a test.

const users = [
  { id: 1, name: 'John' },
  { id: 2, name: 'Jane' }
];
const user = users.find(user => user.id === 2); // { id: 2, name: 'Jane' }

5. some() and every()

some() checks if at least one element passes a test, while every() checks if all elements pass.

const numbers = [1, 2, 3, 4, 5];
const hasEven = numbers.some(num => num % 2 === 0); // true
const allPositive = numbers.every(num => num > 0); // true

Functional Programming Concepts

React encourages a functional programming style. These concepts are important:

1. Immutability

In React, you should treat state as immutable, creating new objects/arrays rather than modifying existing ones.

// Wrong - Mutating the original
const updateUser = (user) => {
  user.name = 'New Name'; // Mutation!
  return user;
};

// Right - Creating a new object
const updateUser = (user) => {
  return { ...user, name: 'New Name' }; // New object
};

// For arrays:
const addItem = (items, newItem) => {
  return [...items, newItem]; // New array
};

const removeItem = (items, id) => {
  return items.filter(item => item.id !== id); // New array
};

const updateItem = (items, id, updates) => {
  return items.map(item =>
    item.id === id ? { ...item, ...updates } : item
  ); // New array
};

2. Pure Functions

Pure functions always return the same output for the same input and have no side effects.

javascript

// Pure function
function add(a, b) {
  return a + b;
}

// Impure function (uses external state)
let count = 0;
function increment() {
  count += 1; // Side effect
  return count;
}

React components should generally be designed as pure functions of their props and state.

3. Higher-Order Functions

Functions that take functions as arguments or return functions.

// Function that returns a function
const multiply = (factor) => {
  return (number) => number * factor;
};

const double = multiply(2);
console.log(double(5)); // 10

In React, higher-order components (HOCs) and custom hooks are based on this concept.

JavaScript Closure

Closures occur when a function retains access to its lexical scope when executed outside that scope.

function createCounter() {
  let count = 0;
  return function() {
    count += 1;
    return count;
  };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2

In React, closures are important for:

  • Event handlers that need access to component state
  • Custom hooks
  • Memoized functions with useCallback and useMemo

The this Keyword and Binding

While less important in modern React with functional components, understanding this is crucial for class components.

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
   
    // Binding to ensure `this` refers to the component
    this.handleClick = this.handleClick.bind(this);
  }
 
  handleClick() {
    this.setState({ count: this.state.count + 1 });
  }
 
  // Alternative: use arrow function (automatically binds `this`)
  handleReset = () => {
    this.setState({ count: 0 });
  }
 
  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.handleClick}>Increment</button>
        <button onClick={this.handleReset}>Reset</button>
      </div>
    );
  }
}

Optional Chaining and Nullish Coalescing

These newer JavaScript features help write more concise code.

Optional Chaining (?.)

Allows reading properties from nested objects without checking if each reference is valid.

// Without optional chaining
const userName = user && user.profile && user.profile.name;

// With optional chaining
const userName = user?.profile?.name;

Nullish Coalescing Operator (??)

Provides a default value when dealing with null or undefined (but not other falsy values).

// Without nullish coalescing
const count = data.count !== undefined && data.count !== null ? data.count : 0;

// With nullish coalescing
const count = data.count ?? 0;

This differs from the logical OR (||) operator, which returns the right-hand operand if the left is any falsy value (including empty strings, 0, etc.).

Putting It All Together: A React Example

Let’s see how these JavaScript features are commonly used in a React component:

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

// User component using modern JavaScript features
const UserProfile = ({ userId }) => {
  // useState hook with destructuring
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  // useEffect with async/await
  useEffect(() => {
    // Async function declaration inside useEffect
    const fetchUser = async () => {
      if (!userId) return;
     
      try {
        setLoading(true);
        setError(null);
       
        const response = await fetch(`https://api.example.com/users/${userId}`);
       
        if (!response.ok) {
          throw new Error('Failed to fetch user data');
        }
       
        const data = await response.json();
        setUser(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchUser();
   
    // Cleanup function
    return () => {
      // Cleanup code if needed
    };
  }, [userId]); // Dependency array

  // Early return pattern with conditional rendering
  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;
  if (!user) return <p>No user data available</p>;

  // Object destructuring
  const { name, email, address } = user;
 
  // Optional chaining and nullish coalescing
  const city = address?.city ?? 'Unknown';
  const zipCode = address?.zipCode ?? 'Unknown';

  return (
    <div className="user-profile">
      <h2>{name}</h2>
      <p>Email: {email}</p>
      <p>Location: {city}, {zipCode}</p>
     
      {/* Conditional rendering with logical && */}
      {user.isAdmin && <span className="admin-badge">Admin</span>}
     
      {/* Array map method for lists */}
      <h3>Skills</h3>
      <ul>
        {user.skills?.map((skill, index) => (
          <li key={index}>{skill}</li>
        ))}
      </ul>
     
      {/* Event handler using arrow function */}
      <button onClick={() => console.log('Profile viewed')}>
        View Full Profile
      </button>
    </div>
  );
};

export default UserProfile;

Summary

Modern JavaScript features are the foundation of React development. In this chapter, we’ve covered:

  • ES6+ features like arrow functions, destructuring, and spread operators
  • Array methods commonly used in React development
  • Functional programming concepts important for React
  • Closures and their role in React
  • The this keyword and proper binding in class components
  • Modern JavaScript features like optional chaining and nullish coalescing
  • A comprehensive example showing how these features work together in a React component

Understanding these JavaScript concepts will make your React code more concise, maintainable, and efficient. In the next chapter, we’ll dive into JSX, the syntax extension used to write UI components in React.

Further Reading

Scroll to Top