ES2023: The Four New Array Mutators (Non-Destructive)


Historically, array methods like sort(), splice(), and reverse() change the array in place (they are destructive/mutating). If you wanted to avoid that, you had to manually copy the array first (const newArr = [...oldArr].sort()).

ES2023 fixes this with new counterparts that return a new array without touching the original:

1. toSorted()

The non-mutating version of sort().

const animals = ['unicorn', 'purple worm', 'dragon'];

// Old (Destructive):
animals.sort(); // Modifies 'animals'

// New (Non-Destructive):
const sortedAnimals = animals.toSorted(); 
// sortedAnimals: ['dragon', 'purple worm', 'unicorn']
// animals: ['unicorn', 'purple worm', 'dragon'] <-- Original array remains unchanged!

2. toReversed()

The non-mutating version of reverse().

const primes = [2, 3, 5, 7];
const reversedPrimes = primes.toReversed(); 
// reversedPrimes: [7, 5, 3, 2]

3. toSpliced(start, deleteCount, ...items)

The non-mutating version of splice(). This is arguably the most useful, as splice was the most complex to replicate manually.

const devs = ['Sergio', 'Ivan', 'Misha', 'Marc'];
// Removes Ivan and adds Carlos
const updatedDevs = users.toSpliced(2, 2, 'Carlos'); 

// updatedDevs: ['Sergio', 'Ivan', 'Carlos']
// devs: ['Sergio', 'Ivan', 'Misha', 'Marc']

4. with(index, value)

This method is the non-mutating way to replace an element at a specific index. It’s similar to array[index] = value but returns a new array.

Maybe in english sounds ok, but if you translate the name to spanish it’s really strange.

const colors = ['red', 'green', 'blue'];
// Replace 'green' (index 1) with 'yellow'
const newColors = colors.with(1, 'yellow');

// newColors: ['red', 'yellow', 'blue']
// colors: ['red', 'green', 'blue']

Extra update: Searching Arrays from the End

The Array.prototype gained methods to search from the end: findLast() and findLastIndex(). While simple, these eliminate the need to manually reverse an array and search, only to reverse the index back later.

const users = [
  { id: 1, name: 'A' }, 
  { id: 2, name: 'B' }, 
  { id: 1, name: 'C' } // Duplicate ID
];

// Easily find the LAST user with id: 1
const lastMatch = users.findLast(u => u.id === 1); 
// lastMatch: { id: 1, name: 'C' }

And that’s all folks! See you on the next post 🙂

Clean Code Alert: The ?. and ?? Operators You Must Use

The Old Way: Unsafe and Messy 😓

We’ve all been there. Trying to safely access data that might be missing in a nested object structure often leads to code pollution and potential runtime errors.

Before modern JavaScript, accessing a property often looked like this defensive block of code:

// This is fragile and hard to read
const companyName = user.job && user.job.company && user.job.company.name;

This checks for every single intermediate property (job, company) to prevent a crash if user.job happens to be null or undefined. It’s verbose and distracting.


1. Optional Chaining: ?. for Safe Access 🔒

The Optional Chaining Operator (?.) is a powerful syntax addition that simplifies the process of reading properties deep within a structure.

If any reference in the chain is null or undefined, the expression immediately stops evaluating and returns undefined, instead of throwing a critical error.

With ?. (The Upgrade):

// Safe, concise, and clean
const companyName = user?.job?.company?.name;

This operator works reliably on object properties, array elements, and even when conditionally calling a function:

const userData = apiResponse?.data?.[0]; // Accesses first item if data exists
const result = apiFunction?.(params);    // Calls function only if it exists

2. Nullish Coalescing: ?? for Reliable Defaults 💡

The Nullish Coalescing Operator (??) solves a common problem with setting default values. It allows you to define a fallback value only when the primary value is strictly null or undefined (known as ‘nullish’).

Why not use || (OR)?

The standard || operator treats valid, zero-value data (0, false, "") as “falsy,” causing it to incorrectly assign the default.

const userAge = 0;
const defaultAge = userAge || 30; // defaultAge becomes 30 (Incorrectly uses default)

const settings = { allowEmails: false };
const isAllowed = settings.allowEmails || true; // isAllowed becomes true (Incorrectly overrides false)

The ?? Fix:

The ?? operator respects valid falsy values like 0, false, or an empty string "".

const userAge = 0;
const actualAge = userAge ?? 30; // actualAge is 0 (Correct)

const settings = { allowEmails: false };
const isAllowed = settings.allowEmails ?? true; // isAllowed is false (Correct)

Use ?? when you need a default, but still want to honor zeros, false booleans, or empty strings that might come from your data.


The Bottom Line: These two operators (?. and ??) aren’t just syntax sugar; they are fundamental tools for writing robust, concise, and professional modern JavaScript.

The Core of React: useState and useEffect Explained Simply

1. useState: Data That Changes Your View 💡

State is simply data that your component needs to remember and watch. When this data changes, React must re-render the component to update the UI.

The useState Hook is how you manage this dynamic data.

How It Works (The Syntax):

When you call useState, it doesn’t just return a value; it returns an array with exactly two items:

  1. The current state value (the actual data).
  2. A setter function to update that value.

We use array destructuring to pull these two items out:

import React, { useState } from 'react';

function Counter() {
  // 1. 'count' is the current value (starts at 0)
  // 2. 'setCount' is the function to change it
  const [count, setCount] = useState(0); 

  const handleClick = () => {
    // DO NOT use count++. Use the setter function!
    setCount(count + 1);
  };

  return (
    <div>
      <p>Clicked {count} times</p>
      <button onClick={handleClick}>Click Me</button>
    </div>
  );
}

The Rule of State:

NEVER modify the state variable directly (count = 5 is forbidden!!!). Always use the setter function (setCount). This is how React knows when to trigger a re-render.


2. useEffect: Running Code After Rendering ⚙️

Every time your component renders (or re-renders due to a state change), React updates the screen. But what if you need to run some code after that update is finished? This is where you use useEffect.

This Hook is built for side effects—operations that interact with the “outside world,” such as:

  • Fetching data from an API.
  • Manually updating the DOM title (document.title).
  • Setting up subscriptions or timers.

How It Works (The Dependency Array):

useEffect takes two arguments:

  1. A function (the code you want to run).
  2. An optional dependency array ([]).

The dependency array is the most important part. It tells React when to re-run the effect:

ArrayWhen the Effect RunsTypical Use Case
[count]Runs after the first render AND whenever count changes.Data fetching dependent on a prop or state.
[]Runs only once after the initial render (like componentDidMount).Setting up event listeners or fetching initial data.
**(No array) **Runs after EVERY single render.Extremely rare; generally causes performance issues.

Example: Updating the Document Title

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

function TitleUpdater() {
  const [value, setValue] = useState('Initial Title');

  // This effect runs whenever 'value' changes.
  useEffect(() => {
    document.title = `Current Value: ${value}`;
    console.log('Document title updated!');
  }, [value]); // Dependency: Re-run when 'value' changes

  return (
    <input 
      type="text" 
      value={value} 
      onChange={(e) => setValue(e.target.value)}
    />
  );
}

The Cleanup Function (The Return):

If your effect sets up something that needs to be torn down (like a subscription or timer), useEffect lets you return a cleanup function. This function runs before the component is destroyed (or before the effect re-runs).

useEffect(() => {
  const timerId = setInterval(() => {
    // do something every second
  }, 1000);

  // RETURN the cleanup function!
  return () => {
    clearInterval(timerId); // Stop the timer when component unmounts
  };
}, []); // Runs once

The Takeaway: useState gives your component a memory, and useEffect lets your component talk to the world outside of React. Master these two, and you’ve mastered the foundation of modern functional components.