1 of 25

Handling Side Effects

Ketan Mayer-Patel

UNC Department of Computer Science

COMP 426: Modern Web Programming (Fall 2026)

Module 2: React

Lecture 10

2 of 25

  • a03: NYT Games is open and will be due Wed, 10/1!
    • Demo
    • Pair project

​

Announcements

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

M2: React

L10

!

3 of 25

  • React components encapsulate UI and state.
  • The state hook (useState) store stateful data and allow components to re-render when state changes occur.

Recap

  • So far, we have been diving into React.

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

M2: React

L10

4 of 25

Clarifying when useState is run

  • useState(0) does get called and run on every render
  • The results are new references to current value and setter function.
    • Initialization only occurs the first time.
  • Plus click handler is created and installed each time.

AppComponent.ts

export const AppComponent = () => {

​

const [counter, setCounter] = useState(0);�

const plus = () => setCounter(counter + 1);

return (

<div>

<p>{ counter }</p>� <button onClick={plus}>� Increase count!� </button>

</div>

);

}

5 of 25

As we have seen, React works with this relationship.

UI / State Relationship

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

Actions on the UI�changes the app’s state.

State changes then update the UI.

M2: React

L10

The UI

State

1

2

6 of 25

  • Consider A03: NYT Games.
  • Many of our apps require loading external data via an API using a fetch call.

Loading External Data

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

Let’s see an example in StackBlitz…

https://go.unc.edu/426f26-L10

M2: React

L10

fetch(`url here`) � .then(res => res.json())� .then(data => {� // Work with data� }

Note: React components are NOT allowed to be async.

7 of 25

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

Review: React Component Lifecycle

M2: React

L10

  • Discuss with a partner / small group
  • Why does the alert show up on the screen every time the button is pressed?
  • What do you think happens if we:
    1. Added a state for the word
    2. Updated the state when the data loads?

​

8 of 25

Whenever a re-render occurs, the entire React component function re-runs.

The Problem

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

Actions on the UI�changes the app’s state.

State changes then update the UI.

M2: React

L10

The UI

State

1

2

Whenever the state changes, the UI is recalculated, calling the entire function body.

9 of 25

Whenever a re-render occurs, the entire React component function re-runs.

The Problem

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

Actions on the UI�changes the app’s state.

State changes then update the UI.

M2: React

L10

The UI

State

1

2

Whenever the state changes, the UI is recalculated, calling the entire function body.

​

We may want to load some data from an external system when a component loads.�Then, have this persist on future re-renders.

Right now, a state is the only way for data to persist on re-render, but that causes an infinite re-rendering loop.

10 of 25

Any action that a component takes that is not rendering the UI (or updating state) is known as a side effect.

Side Effects

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

Actions on the UI�changes the app’s state.

State changes then update the UI.

Component

M2: React

L10

The UI

State

1

2

Side Effect

11 of 25

There are three main cases when a side effect can be triggered.

Side Effects

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

Every time a component renders.

State changes then update the UI.

Component

The first time a component renders.

When specific states change.

M2: React

L10

The UI

State

2

Side Effect

1

2

3

12 of 25

  • We can handle side effects in React using the useEffect() hook.

Handling Side Effects

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

M2: React

L10

useEffect(() => {

// Perform side effect!

});

​

useEffect Hook

13 of 25

  • We can handle side effects in React using the useEffect() hook.
  • useEffect can handle all three triggers of side effects.

Handling Side Effects

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

M2: React

L10

useEffect(() => {

// Perform side effect!

});

​

Run on every render

useEffect(() => {

// Perform side effect!

}, []);

​

Run on first render

useEffect(() => {

// Perform side effect!

}, [state]);

​

Run on state change

14 of 25

  • We can handle side effects in React using the useEffect() hook.
  • useEffect can handle all three triggers of side effects.

Handling Side Effects

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

Let’s see an example in StackBlitz…�

M2: React

L10

useEffect(() => {

// Perform side effect!

});

​

Run on every render

useEffect(() => {

// Perform side effect!

}, []);

​

Run on first render

useEffect(() => {

// Perform side effect!

}, [state]);

​

Run on state change

useEffect takes in an optional dependency array.�If provided, useEffect only re-runs when dependencies (states) passed into the array change. If empty, the effect only runs once.

15 of 25

Examples of Side Effects

  • Fetching data from an external API
  • Setting up event handlers (especially on window)
    • We set up keyboard event handlers by adding handlers to the window!
    • Without useEffect, every re-render would add a new event handler.
  • Storing data (ex: to localStorage)
  • Setting up timers or timeouts

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

M2: React

L10

16 of 25

  • The useEffect() hook also can return a cleanup function that runs before the effect re-runs or when the component unmounts.
    • This is crucial for preventing memory leaks!

Cleanup

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

M2: React

L10

useEffect(() => {

const handler = () => { ... };

window.addEventListener(“scroll”, handler);�� return () => {� window.removeEventListener(“scroll”, handler);� }

}, []);

​

useEffect Hook

Ensures the event handler does not outlive the component!

17 of 25

Warning!!

  • Do not rush to use an effect for everything – you might not need an effect!
    • Specifically, you can still trigger code using events (like onClick).
    • You do not need to use effects to compute properties based on state.�
  • React Docs - You Might Not Need an Effect

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

M2: React

L10

18 of 25

Discuss in pairs�Consider this component to show a list of songs - and that I am a very indecisive listener! I flick through songs to find one I like.

Why might I report that flicking through songs appears to be very sluggish?

Submit thoughts to GradeScope!

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

M2: React

L10

Attendance Check!

!

function SongList({ songs }: { songs: Song[] }) {

​

// Assume that the time complexity of this

// operation is around O(N^3).

const curatedSongs = filterAndSortSongs(songs);

​

const [selectedSong, setSelectedSong]

= useState<Song | null>(null);

​

return <>

{curatedSongs.map((song) => (

<div � key={song.id}

onClick={() => setSelectedSong(song)}

>

{ song.name }

</div>

))}

</>;

};

SongList.ts

19 of 25

We could try to fix this using a useState hook...

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

Solution?

M2: React

L10

function SongList({ songs }: { songs: Song[] }) {

​

// Assume that the time complexity of this

// operation is around O(N^3).

const curatedSongs = filterAndSortSongs(songs);

​

const [selectedSong, setSelectedSong]

= useState<Song | null>(null);

​

return <>

{curatedSongs.map((song) => (

<div � key={song.id}

onClick={() => setSelectedSong(song)}

>

{ song.name }

</div>

))}

</>;

};

SongList.ts

20 of 25

We could try to fix this using a useState hook...

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

Solution?

There is a problem here…

M2: React

L10

function SongList({ songs }: { songs: Song[] }) {

​

// Assume that the time complexity of this

// operation is around O(N^3).

const [curatedSongs, setCuratedSongs]

= useState<Song[]>(filterAndSortSongs(songs));

​

const [selectedSong, setSelectedSong]

= useState<Song | null>(null);

​

return <>...</>;

};

SongList.ts

Now, the value of curatedSongs is not re-calculated on every rerender.

What if songs changes?

21 of 25

We could try to fix this using a useState hook...

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

Solution?

There is a problem here…

If our list of songs changes, we want to recalculate the filtering and sorting.

Any thoughts on how we can do this when the list of songs change?

M2: React

L10

function Playlist() {

...

return <SongList songs={playlist.songs} />

}

​

function SongList({ songs }: { songs: Song[] }) {

​

// Assume that the time complexity of this

// operation is around O(N^3).

const [curatedSongs, setCuratedSongs]

= useState<Song[]>(filterAndSortSongs(songs));

​

const [selectedSong, setSelectedSong]

= useState<Song | null>(null);

​

return <>...</>;

};

SongList.ts

22 of 25

We could try to fix this using a useState hook...

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

Solution?

There is a problem here…

If our list of songs changes, we want to recalculate the filtering and sorting.

Any thoughts on how we can do this when the list of songs change?

M2: React

L10

SongList.ts

function Playlist() {

...

return <SongList songs={playlist.songs} />

}

​

function SongList({ songs }: { songs: Song[] }) {

​

// Assume that the time complexity of this

// operation is around O(N^3).

const [curatedSongs, setCuratedSongs]

= useState<Song[]>(filterAndSortSongs(songs));

​

useEffect(() => {

setCuratedSongs(filterAndSortSongs(songs));

}, [songs]);

​

const [selectedSong, setSelectedSong]

= useState<Song | null>(null);

​

return <>...</>;

};

23 of 25

Preserving (caching) the result of an “expensive operation” every re-render and only recalculating it when needed is known as memoization.

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

BONUS

M2: React

L10

SongList.ts

Very common optimization across various types of software engineering projects!

function Playlist() {

...

return <SongList songs={playlist.songs} />

}

​

function SongList({ songs }: { songs: Song[] }) {

​

// Assume that the time complexity of this

// operation is around O(N^3).

const [curatedSongs, setCuratedSongs]

= useState<Song[]>(filterAndSortSongs(songs));

​

useEffect(() => {

setCuratedSongs(filterAndSortSongs(songs));

}, [songs]);

​

const [selectedSong, setSelectedSong]

= useState<Song | null>(null);

​

return <>...</>;

};

24 of 25

Preserving (caching) the result of an “expensive operation” every re-render and only recalculating it when needed is known as memoization.

Handling Side Effects

COMP 426: Modern Web Programming (Fall '26)

BONUS

M2: React

L10

SongList.ts

React has a special hook called useMemo that helps combine the goals of using useState and useEffect in one hook.

function Playlist() {

...

return <SongList songs={playlist.songs} />

}

​

function SongList({ songs }: { songs: Song[] }) {

​

// Assume that the time complexity of this

// operation is around O(N^3).

const curatedSongs = useMemo(() => {

return filterAndSortSongs(songs);

}, [songs]);

const [selectedSong, setSelectedSong]

= useState<Song | null>(null);

​

return <>...</>;

};

25 of 25

Example by Claude

https://go.unc.edu/426f26-L10-useMemo

​

Create a small React application intended to be run on StackBlitz as a teaching example. The application should present a listing of songs using a SongList component that accepts an array of Song objects and a filtering function as props. SongList should apply the filtering function to the list and produce a scrollable listing of the results. The SongList component should have a React state variable keeping track of a selected song which is updated when a particular Song in the listing is clicked on. The filtering function should take an artificially long amount of time such that it will be noticeable if performed on each rerender of SongList when a Song is selected. The lesson to be taught is about using the useMemo hook to cache expensive results that should only be recalculated if the filter options are changed.