Skip to content

Exercises

A pure grid

Below, you'll find a customizable two-dimensional grid. In the same application, we're tracking the user's mouse position, and displaying it above the grid.

Your task is to optimize the grid so that it doesn't have to re-render when the user's mouse position changes.

Acceptance Criteria:

  • Grid should only re-render when either numRows or numCols changes. Moving the mouse across the viewport should not re-render the Grid component.

Code Playground

import React from 'react';

import Grid from './Grid';

function App() {
const [
mousePosition,
setMousePosition,
] = React.useState({ x: 0, y: 0 });

const [numRows, setNumRows] = React.useState(12);
const [numCols, setNumCols] = React.useState(12);
const id = React.useId();

React.useEffect(() => {
function handleMouseMove(event) {
setMousePosition({
x: event.clientX,
y: event.clientY,
});
}

window.addEventListener('mousemove', handleMouseMove);

return () => {
window.removeEventListener(
'mousemove',
handleMouseMove
);
};
}, []);

return (
<>
<form>
<div>
<label htmlFor={`${id}-rows`}>Rows:</label>
<input
id={`${id}-rows`}
type="range"
value={numRows}
onChange={(event) => setNumRows(event.target.value)}
min={5}
max={40}
/>
</div>
<p>
{mousePosition.x} / {mousePosition.y}
</p>
<div>
<label htmlFor={`${id}-cols`}>Columns:</label>
<input
id={`${id}-cols`}
type="range"
value={numCols}
onChange={(event) => setNumCols(event.target.value)}
min={5}
max={40}
/>
</div>
</form>
<Grid numRows={numRows} numCols={numCols} />
preview
console
  1. Grid render
  2. Grid render

Solution:

Shopping cart

Let's revisit the “Shopping Cart” exercise back from Module 1!

Update the code so that CartTable doesn't re-render unnecessarily.

Acceptance Criteria:

  • Editing the postal code should not trigger a re-render in the CartTable component.

Code Playground

import React from 'react';

import CartTable from './CartTable';

function ShoppingCart({ items }) {
const [postalCode, setPostalCode] = React.useState('');
const postalCodeId = React.useId();

const inStockItems = items.filter(
(item) => item.inStock
);
const outOfStockItems = items.filter(
(item) => !item.inStock
);

return (
<>
<h2>Shopping cart</h2>
<form>
<label htmlFor={postalCodeId}>
Enter Postal / ZIP code for shipping estimate:
</label>
<input
id={postalCodeId}
type="text"
value={postalCode}
onChange={(event) => {
setPostalCode(event.target.value);
}}
/>
</form>
<CartTable items={inStockItems} />

<div className="actions">
<button>Continue checkout</button>
</div>

<h2>Sold out</h2>
<CartTable items={outOfStockItems} />
</>
);
}

export default ShoppingCart;
preview
console
  1. CartTable render
  2. CartTable render
  3. CartTable render
  4. CartTable render

Solution:

Memoized clock toggle

In the “Custom Hooks” exercises, we created a handy-dandy useToggle hook:

function useToggle(initialValue = false) {
if (typeof initialValue !== 'boolean') {
console.warn('Invalid type for useToggle');
}
const [value, setValue] = React.useState(
initialValue
);
function toggleValue() {
setValue((currentValue) => !currentValue);
}
return [value, toggleValue];
}

We've updated our digital clock application to use this hook.

In the sandbox below, you'll find that everything works well... but our ClockToggle component is rendering on every state change, even ones that don't affect it.

Update the code so that ClockToggle doesn't re-render unnecessarily.

Acceptance Criteria:

  • The ClockToggle component should become a pure component
  • ClockToggle should not re-render when the time or showClock state variables change.

Code Playground

import React from 'react';

import useToggle from './hooks/use-toggle';
import Clock from './Clock';
import ClockToggle from './ClockToggle';

function App() {
const [time, setTime] = React.useState(new Date());
const [showClock, toggleClock] = useToggle(true);
React.useEffect(() => {
const intervalId = window.setInterval(() => {
setTime(new Date());
}, 1000);
return () => {
window.clearInterval(intervalId);
};
}, []);
return (
<>
{showClock && <Clock time={time} />}
<ClockToggle handleToggle={toggleClock} />
</>
);
}

export default App;
preview
console
  1. ClockToggle render
  2. ClockToggle render
  3. ClockToggle render
  4. ClockToggle render
  5. ClockToggle render
  6. ClockToggle render
  7. ClockToggle render
  8. ClockToggle render
  9. ClockToggle render
  10. ClockToggle render

Solution:

Note: I filmed this solution video before Strict Mode was built into the sandbox, and so it only shows one “ClockToggle render” log per tick instead of two. Everything else, however, should be the same regardless.