Skip to content

Cleanup Exercises

Fixing previous exercises

Now that we know how to manage cleanup, let's improve our solutions from previous exercises!

Window dimensions

Update our solution to the “Window dimensions” exercise so that the event listener is cleaned up.

Acceptance Criteria:

  • The "resize" event listener should be removed inside a cleanup function.

Code Playground

import React from 'react';

function WindowSize() {
const [
windowDimensions,
setWindowDimensions,
] = React.useState({
width: window.innerWidth,
height: window.innerHeight,
});

React.useEffect(() => {
function handleResize() {
console.log('resize');
setWindowDimensions({
width: window.innerWidth,
height: window.innerHeight,
});
}

window.addEventListener('resize', handleResize);
}, []);

return (
<div className="wrapper">
<p>
{windowDimensions.width} / {windowDimensions.height}
</p>
preview
console

Solution:

Toasty!

In the “Toasty!” exercise, we implemented an animation using IntersectionObserver, but we never cancel this process!

Here's the code required to stop an IntersectionObserver:

observer.disconnect()

Acceptance Criteria:

  • The IntersectionObserver should be disconnected within the effect's cleanup function.

Code Playground

/*

To stop an IntersectionObserver, you can run:
observer.disconnect();

*/
import React from 'react';

import styles from './Toasty.module.css';

function Toasty() {
const [isShown, setIsShown] = React.useState(false);

const wrapperRef = React.useRef();

React.useEffect(() => {
const observer = new IntersectionObserver((entries) => {
const [entry] = entries;

setIsShown(entry.isIntersecting);
});

observer.observe(wrapperRef.current);
}, []);

const translateX = isShown ? '0%' : '100%';

return (
<div ref={wrapperRef} className={styles.wrapper}>
<div
className={styles.character}
style={{
transform: `translateX(${translateX})`,
}}
>
👻
</div>
</div>
);
}

export default Toasty;

Solution:

Digital clock

Let's build a digital clock with React!

Below, you'll find a clock that initializes at the correct value, but it doesn't update. Your mission is to update the code so that the clock shows the correct time:

10:30:09 PM

Acceptance Criteria:

  • The clock should update once per second, to show the current time.
  • There should be no memory leaks, no ongoing processes that outlive the Clock instance.

Code Playground

import React from 'react';
import format from 'date-fns/format';

function Clock() {
const [time, setTime] = React.useState(new Date());

return (
<p className="clock">
{format(time, 'hh:mm:ss a')}
</p>
);
}

export default Clock;
preview
console

Solution:

Useless machine

Have you ever heard of “useless machines”? These small boxes are built by hobbyists, and have a single switch on them. When you flip the switch, the box flips it back:

It's a completely useless machine! But it's fun and whimsical.

Let's build something similar. We'll create a checkbox that automatically ticks itself, whenever it's unticked:

Acceptance Criteria:

  • When the user unticks the checkbox, it should become re-ticked automatically after half a second. This can be done with a setTimeout.
  • If the user toggles it off and on really quickly, the timeout should be canceled, since the machine has been left in an "on" state.
  • If the user clicks it a bunch of times in rapid succession, the machine should wait a full 500ms from the final click before ticking itself back on.

(Your checkbox might look a little bit different, depending on browser/OS.)

Code Playground

import React from 'react';

import VisuallyHidden from './VisuallyHidden';

function UselessMachine() {
const id = React.useId();
const [isOn, setIsOn] = React.useState(true);
return (
<>
<input
id={id}
type="checkbox"
checked={isOn}
onChange={event => {
setIsOn(event.target.checked);
}}
/>
<VisuallyHidden>
<label htmlFor={id}>
Toggle checkbox
</label>
</VisuallyHidden>
</>
);
}

export default UselessMachine;

Solution: