Skip to content
React Course1.0.0 beta
CSEN
React Course1.0.0 beta
CSEN
🏠 Home🧭 Where to start?🔁 Review📄 Cheat sheets📰 What's newℹ️ About the course
0. JavaScript0/22

JavaScript in general

  • 0.1 Variables, types and operators0/2
  • 0.2 Functions and closures0/2
  • 0.3 Objects0/2
  • 0.4 Arrays I: map, filter and friends0/2
  • 0.5 Arrays II: reduce, sorting and grouping0/3
  • 0.6 Conditions and logical operators0/2
  • 0.7 Asynchronous JavaScript0/2
  • 0.8 Modules and built-in tools0/2

JavaScript for React

  • 0.9 References and immutability0/3
  • 0.10 JS patterns you meet everywhere in React0/2

Basics

  • 1. A quick review of the basics0/3
  • 2. State and events (useState)0/3

Hooks in depth

  • 3. Effects (useEffect)0/3
  • 4. Refs (useRef)0/3
  • 5. More complex state (useReducer)0/3
  • 6. Context0/3
  • 7. Custom hooks0/3

Forms

  • 8. Forms and React 19 Actions0/3

Performance

  • 9. Performance and rendering0/3

Patterns

  • 10. Component design patterns0/3

Ecosystem

  • 11. Where the type goes (TypeScript syntax)0/3
  • 12. TypeScript with React0/3
  • 13. Routing (React Router)0/3
  • 14. Data fetching0/3
  • 15. Application state management0/3

Project

  • 16. Final project: Kanban0/2

Summary

  • 17. Summary: the principles of React

Your account

Privacy

Lesson 0.6 · JavaScript in general

Conditions and logical operators

Truthy and falsy, the ternary, &&, ||, ??, ?., switch and early return.

Loading lesson…

💬 Found a mistake, something unclear, or have an idea? Let me know.
← Arrays II: reduce, sorting and groupingAsynchronous JavaScript →

Before we start: railway switches

A program runs like a train on rails – line after line. Conditions are the switches: depending on a question, they send the train one way or the other. "Is the user signed in?" "Is the cart empty?" "Has the data arrived from the server?"

JavaScript has two groups of tools for switches. Statements (if, switch) control which lines run. Operators (? :, &&, ||, ??, ?.) pick a value right inside an expression – and those are the ones you'll meet most in React, because you can't write an if inside JSX.

ℹ️ What's in this lesson
  1. if, else and early return.
  2. Truthy and falsy: what counts as "yes".
  3. The logical operators and, or, not.
  4. The ternary operator.
  5. && and || for picking a value.
  6. ?? and ?.: safe default values and reading.
  7. switch and a dictionary instead of long conditions.

1. if, else and early return

const age = 17
if (age >= 18) {
console.log('Adult')
} else if (age >= 13) {
console.log('Teenager')
} else {
console.log('Child')
}
// The one-line form – without braces only for a single statement
if (age < 0) return

Early return instead of a pyramid

When a function has to check several things, don't nest the conditions inside each other. Deal with the exceptions first and stop, and leave the main case for the end:

// ❌ A pyramid
function checkout(cart, user) {
if (user) {
if (cart.length > 0) {
if (user.address) {
return sendOrder(cart, user)
} else {
return 'The address is missing'
}
} else {
return 'The cart is empty'
}
} else {
return 'Please sign in'
}
}
// ✅ Early return – reads top to bottom like a checklist
function checkout(cart, user) {
if (!user) return 'Please sign in'
if (cart.length === 0) return 'The cart is empty'
if (!user.address) return 'The address is missing'
return sendOrder(cart, user)
}

2. Truthy and falsy

When JavaScript needs a yes/no (in an if, a ternary, with &&) and gets something other than a boolean, it converts it. There are only six false (falsy) values – everything else is true (truthy):

Falsy – counts as NOTruthy – counts as YES (even when it doesn't look like it)
falsetrue
042, -1, 0.5
'' (empty text)'0', 'false', ' ' (a space)
null[] (an empty array)
undefined{} (an empty object)
NaNevery function, every date…
if ('hello') { … } // runs
if ('') { … } // doesn't run
if (0) { … } // doesn't run
if ([]) { … } // ⚠️ runs – an empty array is truthy!
if (items.length > 0) // ✅ the right question "does the array contain anything?"
if (items.length) // works too (0 is falsy), but it's less clear what you're asking
if (Object.keys(obj).length > 0) // "does the object have any properties?"
Boolean('hello') // true – conversion to a boolean
!!'hello' // true – the same as a shortcut (double negation)

3. Logical operators: and, or, not

const age = 25
const hasTicket = true
age >= 18 && hasTicket // true – AND: both must hold
age < 18 || hasTicket // true – OR: one is enough
!hasTicket // false – NOT: flips the value
// Combinations – parentheses for safety and readability
const canEnter = (age >= 18 && hasTicket) || isVip
const isWorkday = day !== 'saturday' && day !== 'sunday'
const isWeekend = ['saturday', 'sunday'].includes(day) // often easier to read
✨ Give complex conditions a name
// ❌ What does this mean?
if (user && (user.role === 'admin' || post.authorId === user.id) && !post.locked) { … }
// ✅ Named intermediate results
const isAuthor = user?.id === post.authorId
const isAdmin = user?.role === 'admin'
const canEdit = (isAuthor || isAdmin) && !post.locked
if (canEdit) { … }

4. The ternary operator

A ternary is an if/else squeezed into an expression: condition ? valueIfYes : valueIfNo. Unlike if, it has a value, so you can store it in a variable or put it anywhere in an expression.

const label = isDone ? 'Done' : 'To do'
const price = isMember ? basePrice * 0.9 : basePrice
const greeting = `Good ${hour < 12 ? 'morning' : 'afternoon'}`
const className = `btn ${isActive ? 'btn-active' : ''}`
// The same with if – longer and needs let
let label
if (isDone) {
label = 'Done'
} else {
label = 'To do'
}
⚠️ Nested ternaries
// ❌ A riddle
const size = n > 100 ? 'large' : n > 10 ? 'medium' : n > 0 ? 'small' : 'none'
// ✅ A function with early return
function sizeOf(n) {
if (n > 100) return 'large'
if (n > 10) return 'medium'
if (n > 0) return 'small'
return 'none'
}
One ternary is readable, two nested ones are survivable with good formatting, three aren't.

5. && and || for picking a value

Here's the catch: in JavaScript, && and || don't return true/false. They return one of their sides:

ExpressionWhen a is truthyWhen a is falsy
a && breturns breturns a
a || breturns areturns b
'hello' && 'world' // 'world'
'' && 'world' // ''
0 && 'world' // 0 ← careful, the number 0!
null && 'world' // null
'hello' || 'default' // 'hello'
'' || 'default' // 'default'
0 || 'default' // 'default' ← the zero "got lost"
// Typical use
user && user.name // read name only if user exists (today rather user?.name)
const name = input || 'Anonymous' // a default value for an empty input
isLoggedIn && showWelcome() // call it only when the condition holds
⚠️ The zero trap
In React, && is used for conditional rendering. When the left side is 0, the result is 0 – and React renders the zero on the page. That's why a real boolean always belongs in &&: count > 0 && …, not count && ….

6. ?? and ?.: missing values

?? – a default value only for "nothing"

a ?? b returns b only when a is null or undefined. Zero, empty text and false are valid values for it.

const settings = { volume: 0, nickname: '', notifications: false }
settings.volume || 50 // 50 ❌ the user muted it to 0 and we turned it up to 50
settings.volume ?? 50 // 0 ✅
settings.notifications || true // true ❌ the user turned them off
settings.notifications ?? true // false ✅
settings.theme ?? 'light' // 'light' – the property is missing → the default value
// Assignment with a default value
options.retries ??= 3 // set it to 3 only if there's nothing there
Value aa || "x"a ?? "x"
0"x"0
''"x"''
false"x"false
null"x""x"
undefined"x""x"
'hello''hello''hello'
❓ So when ||, and when ??

Almost always ?? – it only replaces what's really missing. Use || when you also want to skip empty text or zero: say, "the nickname, and when it's empty, the full name" (nickname || name).

?. – read only if there's something to read from

const user = { name: 'Anna', address: null }
user.address.city // 💥 TypeError: Cannot read properties of null
user.address?.city // undefined – ?. stops and returns undefined
user.contacts?.[0] // undefined – works with square brackets too
user.getAge?.() // undefined – and with calling a function that may not exist
// The typical pair: read safely + a default value
const city = user.address?.city ?? 'unknown'
// Data from the server that hasn't arrived yet
const firstTitle = response?.data?.items?.[0]?.title ?? 'No items'
⚠️ Don't write ?. everywhere “just in case”
?. belongs where a value really can be missing (a signed-out user, data not loaded yet, an optional field). Where it shouldn't be missing, it's better for the code to crash – you see the bug straight away instead of undefined quietly spreading.
Demo

Operator lab

What you see: A row of values for x and, below it, what converting to a boolean, a ternary, &&, || and ?? make of each one.

Try it:

  1. Pick 0. x && 'right' returns 0 (not false!), || replaces the zero, ?? keeps it.
  2. Pick '' and then null. For empty text || and ?? differ, for null they don't.
  3. Pick [] and {}. Both are truthy, even though they're "empty".

The takeaway: There are only six falsy values. && and || return one of the sides, not a boolean. ?? only reacts to null and undefined.

Loading the interactive part…

7. switch and a dictionary

When you compare one value with many options, switch is clearer than a chain of ifs:

function statusLabel(status) {
switch (status) {
case 'new':
return 'New order'
case 'paid':
return 'Paid'
case 'shipped':
case 'delivered': // several cases with the same result
return 'On its way'
default:
return 'Unknown status'
}
}
⚠️ Without return or break it falls through to the next case
switch (level) {
case 'warn':
console.log('Warning') // ❌ break is missing → 'Error' is printed too
case 'error':
console.log('Error')
break
}

In functions that just turn one value into another, a dictionary (an object) is often even shorter:

const STATUS_LABELS = { new: 'New order', paid: 'Paid', shipped: 'On its way' }
const statusLabel = (status) => STATUS_LABELS[status] ?? 'Unknown status'
const ICONS = { success: '✅', error: '❌', warning: '⚠️' }
const icon = ICONS[type] ?? 'ℹ️'

You'll see switch, on the other hand, in React reducers (lesson 5), where every action does something different, not just returns a value.

Check yourself

Quiz What does 0 && 'hello' return?
Quiz settings.volume is 0. What is settings.volume || 50?
Quiz Which value is truthy?
Quiz user is null. What does user?.name ?? 'Guest' return?

Warm-up

Challenge

Who is who

Warm-up

One ternary and one ??.

Task (the same text is in the comment at the top of the file)
CHALLENGE: Who is who

1. ageLabel(20) should return 'adult', ageLabel(12) 'child'. The limit is 18. Use a ternary:
   condition ? 'adult' : 'child'
2. nameOrGuest('Anna') should return 'Anna', nameOrGuest(null) 'Guest'. Use ??

Loading the interactive part…

Challenge

Challenge

Safe reading and decisions

Medium Bonus challenge

Six functions that make decisions and safely read data that may be missing. Watch out for the difference between || and ?? – the tests will check it.

🔒 Bonus challenge

Medium and hard challenges are a bonus for signed-in readers. Signing up is free – just an e-mail, no password.

How we handle your data

Summary

  • Early return deals with the exceptions at the start of a function and saves you from if/else pyramids.
  • Falsy is only false, 0, '', null, undefined and NaN. An empty array and an empty object are truthy.
  • A ternary a ? b : c is an if/else that returns a value. Don't nest more than one.
  • && and || return one of the sides, not a boolean – 0 && x is 0.
  • ?? fills in a default value only for null/undefined, || also for 0 and ''. You almost always want ??.
  • ?. safely reads from a value that may be missing. Typically a?.b ?? default.
  • switch for many cases with different logic, a dictionary for turning one value into another.
🧩 Where do the things not defined here come from (Checks, Check)
Checks, Check – from the file src/course/checks.tsx. The course helper for function-style challenges: runs the tests and shows ✅/❌. You don't need to know how it works inside – it just tells you which steps are done.
/** One test: a description and a function returning true (passed) or false. It may be async. */
export type Check = [name: string, test: () => boolean | Promise<boolean>]
/**
* Runs the tests of a challenge and shows ✅ or ❌ for each.
* An error thrown in a test (e.g. when trying to change a frozen object) is shown under the test.
*/
export function Checks({ tests }: { tests: Check[] }) {
const t = useT()
const [results, setResults] = useState<Result[]>(() => tests.map(() => ({ status: 'pending' })))
useEffect(() => {
let ignore = false
tests.forEach(([, test], i) => {
Promise.resolve()
.then(test)
.then(
(ok): Result => (ok ? { status: 'ok' } : { status: 'fail' }),
(e): Result => ({ status: 'fail', error: e instanceof Error ? e.message : String(e) }),
)
.then((result) => {
if (ignore) return
setResults((prev) => prev.map((r, idx) => (idx === i ? result : r)))
})
})
return () => {
ignore = true
}
}, [tests])
const passed = results.filter((r) => r.status === 'ok').length
return (
<div className="stack">
<strong className={passed === tests.length ? 'ok' : undefined}>
{t.checksPassed(passed, tests.length)}
</strong>
<ul className="list">
{tests.map(([name], i) => {
const r = results[i] ?? { status: 'pending' }
return (
<li key={name}>
{r.status === 'pending' ? '⏳' : r.status === 'ok' ? '✅' : '❌'} {name}
{r.status === 'fail' && r.error && (
<div className="bad muted">
{t.checksError} {r.error}
</div>
)}
</li>
)
})}
</ul>
</div>
)
}