C.W.K.
Stream
Lesson 01 of 07 · published

Modern JS in 15 Minutes

~15 min · javascript, es2020, refresher, prerequisite

Level 0React Novice
0 XP0/54 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
This isn't a JS course. It's a check that the React-shaped slice of modern JS lives in your fingers before we open Vite.

The slice React leans on hardest

React 19 in TypeScript barely looks like the JavaScript you'd see in a 2015 jQuery tutorial. Every line we'll write in this quest will hit at least one of these features. If you've been working in modern JS already, this lesson is a victory lap. If you've been away for a while, treat the gaps as homework — the rest of the quest assumes them.

Destructuring and spread

You'll see const { children, className, ...rest } = props in literally every component. The rest-spread pattern is how typed React components forward unknown props to inner elements without listing each one. Destructuring also works for arrays (const [count, setCount] = useState(0) is destructuring assignment plus a returned tuple).

Arrow functions and implicit return

Arrow functions don't have their own this — they capture the surrounding scope. That's why every React event handler you write as an inline arrow function works correctly without bind(). Implicit return ((x) => x * 2) is the basis for the .map(item => <Row {...item} />) pattern.

Template literals and tagged templates

Backticks let you interpolate values and span multiple lines. Tagged templates (a function called with a template literal) power libraries like styled-components and the cn helpers you'll write in Track 4.

Promises, async/await, fetch

Every data hook, every Server Action, every use() call sits on top of a Promise. Async functions are sugar over Promise chains. await only works inside async functions (or at the top level of an ES module). The fetch() API returns a Promise that resolves to a Response — and response.json() returns yet another Promise. You'll see chains like await (await fetch(url)).json() often enough that the double-await stops looking weird.

Optional chaining and nullish coalescing

user?.profile?.name short-circuits on null or undefined at any link. value ?? "default" falls back only on null or undefined (unlike || which also triggers on 0, "", and false). Once you get the muscle memory, you'll stop writing defensive if ladders.

ES modules

Every file in a Vite project is an ES module. import and export aren't sugar — they're the actual module system the browser and Node both run. No require() in modern JS code unless you're maintaining legacy.

If any of this feels foreign: stop and run through the MDN links below. The rest of this quest assumes these features the way a chess book assumes you know how the knight moves. There's no shame in pausing — there is shame in pretending you remember and then debugging a stack trace that's actually a destructuring gotcha.

Code

The features in one component·tsx
// Every modern-JS feature this lesson named, in one tiny React component.
import { useState } from "react";

type MessageProps = {
  user: { profile?: { name?: string } };
  className?: string;
};

export function Greeting({ user, className, ...rest }: MessageProps) {
  // optional chaining + nullish coalescing
  const name = user?.profile?.name ?? "friend";

  // destructured tuple return from useState
  const [waves, setWaves] = useState(0);

  // arrow function with implicit return + template literal
  const label = `Hello, ${name} — waved ${waves} times`;

  return (
    <div className={className} {...rest}>
      <p>{label}</p>
      <button onClick={() => setWaves((n) => n + 1)}>Wave</button>
    </div>
  );
}
async/await + fetch — the data shape you'll see everywhere·ts
type Conversation = { id: string; title: string };

async function loadConversations(): Promise<Conversation[]> {
  const response = await fetch("/api/conversations");
  if (!response.ok) {
    throw new Error(`Failed: ${response.status} ${response.statusText}`);
  }
  // The response body is also a Promise — that's why .json() is awaited.
  const data = (await response.json()) as Conversation[];
  return data;
}

// Top-level await works inside ES modules.
const conversations = await loadConversations();
console.log(`Loaded ${conversations.length} conversations`);

External links

Exercise

Rewrite this snippet using destructuring, optional chaining, nullish coalescing, and an arrow function: function getName(user) { if (user && user.profile && user.profile.name) { return user.profile.name; } else { return 'guest'; } }. Aim for one line in the function body.
Hint
const getName = (user) => user?.profile?.name ?? 'guest'; — three modern-JS features, one line.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.