react

How to quickly listen for a route/page change in Next.js

To detect a route change in Next.js:

  • app directory: use useEffect and usePathname.
  • pages directory: use useEffect and useRouter.

Listen for route/page change in Next.js app directory

To listen for a route change in Next.js 13 app directory, use the useEffect and the usePathname hooks. The action in useEffect will run anytime the pathname from usePathname changes.

src/app/route-change-listener.tsx
'use client'; import { usePathname } from 'next/navigation'; import { useEffect, useState } from 'react'; export function RouteChangeListener() { const pathname = usePathname(); const [changes, setChanges] = useState(0); useEffect(() => { console.log(`Route changed to: ${pathname}`); setChanges((prev) => prev + 1); }, [pathname]); return <></>; }

Here we log to the console and change the state of the component when the route changes.

The URL change listener logs to the console when the route changes.
The URL change listener logs to the console when the route changes.

Page change listener must persist with client-side routing

For this detection to work, the component containing this useEffect needs to be somewhere in the DOM where it will not get unmounted with client-side navigation.

In the Next.js 13 app directory, this could be the layout.tsx file:

src/app/layout.tsx
import { Metadata } from 'next'; import '@/styles/globals.css'; import { RouteChangeListener } from './route-change-listener'; export const metadata: Metadata = { title: 'Coding Beauty', description: 'The official Coding Beauty home page.', icons: { icon: '/favicon.png', }, }; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="en"> {/* 👇 Persists with client-side navigation */} <RouteChangeListener /> <body>{children}</body> </html> ); }

useEffect needs 'use client'

Also, since server components are the default in Next.js 13, you’ll need to add the 'use client' directive at the top of the component file.

Otherwise, you won’t be able to use interactive client-side features like React hooks, including useEffect.

The useEffect React hook can't work without the 'use client' directive in Next.js.
useEffect can’t work without ‘use client’ in Next.js.

Listen for route/page change in Next.js pages directory

To handle a URL or location change in Next.js pages directory, combine the useEffect and the useRouter hooks:

src/pages/url-change-listener.tsx
import { useEffect } from 'react'; import { useRouter } from 'next/router'; export function UrlChangeListener() { const router = useRouter(); useEffect(() => { console.log(`The page is now: ${router.pathname}`); }, [router]); return <></>; }

Route change detector must persist with client-side routing

Just like with the app directory, the component that listens for the URL change with useEffect needs to be somewhere in the DOM where it will not get unmounted with client-side navigation.

In the pages directory, this could be the _app.tsx or _app.js file:

src/pages/_app.tsx
import '@/styles/globals.css'; import type { AppProps } from 'next/app'; import { UrlChangeListener } from './url-change-listener'; export default function App({ Component, pageProps }: AppProps) { return ( <> <UrlChangeListener /> <Component {...pageProps} /> </> ); }

Detect route/page with useRouter() events

Alternatively, we can detect a client-side URL change in the Next.js pages directory with events that the useRouter() object emits. For example:

src/pages/url-change-listener.tsx
import { useEffect } from 'react'; import { useRouter } from 'next/router'; export function UrlChangeListener() { const router = useRouter(); useEffect(() => { const startHandler = () => { console.log('Router change started'); }; const completeHandler = () => { console.log('Router change completed'); }; router.events.on('routeChangeStart', startHandler); router.events.on('routeChangeComplete', completeHandler); return () => { router.events.off('routeChangeStart', startHandler); router.events.off('routeChangeComplete', completeHandler); }; }, []); // 👇 You can put a progress bar or something here return <></>; }

Here we used two important router events:

  • routeChangeStart – fires when the route is about to change.
  • routerChangeComplete – fires when the router has changed completely.

There’s more too, and their names are just as self-explanatory as these two.

The `useRouter()` object has other properties apart from routeChangeStart and routeChangeComplete.

Key takeaways

  • To listen for a route or page change in Next.js app directory, combine the useEffect and usePathname hooks.
  • Detect a URL change in the pages directory with the useEffect and useRouter hooks.
  • You can also use the routeChangeStart and routeChangeComplete events from the useRouter() object to handle a location change.

How to check if a variable is null or undefined in React

To check if a variable in null or undefined in React, use the || operator to check if the variable is equal to null or equal to undefined.

JavaScript
import { useState } from 'react'; export default function App() { const [variable] = useState(undefined); const [message, setMessage] = useState(undefined); const nullOrUndefinedCheck = () => { // Check if undefined or null if (variable === undefined || variable === null) { setMessage('Variable is undefined or null'); } else { setMessage('Variable is NOT undefined or null'); } // Check if NOT undefined or null if (variable !== undefined && variable !== null) { setMessage('Variable is NOT undefined or null'); } }; return ( <div> <button onClick={() => nullOrUndefinedCheck()}>Check variable</button> <h2>{message}</h2> </div> ); }
Checking if a variable is null or undefined in React.

The logical OR (||) operator

The logical OR operator (||) results in true if either the value at the left or right is true.

JavaScript
const isTrue = true; const isFalse = false; const result1 = isTrue || isFalse; console.log(result1); // Output: true const result2 = isFalse || isFalse; console.log(result2); // Output: false

When you use || on non-boolean values, it returns the first truthy value:

JavaScript
const str = ''; const num = 0; const result1 = str || 'Default String'; console.log(result1); // Output: 'Default String' const result2 = num || 42; console.log(result2); // Output: 42

The falsy values in JavaScript are undefined, null, 0, NaN, '' (empty string), and false. Every other value is truthy.

Checking if NOT null or undefined with De Morgan’s laws

From De Morgan’s law, not (A or B) = not A and not B, where A and B are boolean conditions.

That’s why we used this condition to check if the variable is not null or undefined:

JavaScript
// not A and not B if (variable !== undefined && variable !== null) { setMessage('✅ Variable is NOT undefined or null'); }

And why we could just as easily do this:

JavaScript
// not (A or B) if (!(variable === undefined || variable === null)) { setMessage('✅ Variable is NOT undefined or null'); }

null and undefined are the same with ==

It’s important you use === over == for the check, if you want null and undefined to be treated separately. == is known as the loose equality operator, can you guess why it has that name?

JavaScript
console.log(null == undefined); // true console.log(null === undefined); // false

Loose equality fails in cases where undefined and null don’t mean the same thing.

Imagine a shopping cart application where a user adds items the cart, represented by a cartItems variable.

  • If cartItems is undefined, it’ll mean that the shopping cart hasn’t been initialized yet, or an error occurred while fetching it, and we should load this data before proceeding.
  • If cartItems is null, it’ll mean that the shopping cart is empty – the user hasn’t added any items yet.

Here’s a simplified example of how you might handle this in a React component:

JavaScript
import React, { useEffect, useState } from 'react'; const ShoppingCart = () => { const [cartItems, setCartItems] = useState(); // ... useEffect(() => { // Assume fetchCartItems() returns a promise that resolves to the cart items fetchCartItems() .then((items) => setCartItems(items)) .catch(() => setCartItems(null)); }, []); if (cartItems === undefined) { return <div>Loading cart items...</div>; } else if (cartItems === null) { return <div>Your shopping cart is empty! Please add some items.</div>; } else { return ( <div> <h2>Your Shopping Cart</h2> {cartItems.map((item) => ( <p key={item.id}>{item.name}</p> ))} </div> ); } }; export default ShoppingCart;

If we had used the == operator, only the first comparison to undefined or null would have run.

Check for null, undefined, or anything falsy in React

Instead of checking explicitly for null or undefined, you might be better off checking if the variable is falsy.

JavaScript
import { useState } from 'react'; export default function App() { const [variable] = useState(undefined); const [message, setMessage] = useState(undefined); const falsyCheck = () => { // Check if falsy if (!variable) { setMessage('✅ Variable is falsy'); } else { setMessage('❌ Variable is NOT falsy'); } }; return ( <div> <button onClick={() => falsyCheck()}>Check variable</button> <h2>{message}</h2> </div> ); }

By passing the variable to the if statement directly, it is coalesced to a true or false boolean depending on whether the value is truthy or falsy.

The falsy values in JavaScript are undefined, null, 0, NaN, '' (empty string), and false. Every other value is truthy.