cf.completefrontend

Write the codeEasy

Rank a leaderboard only when the players or the sort order change

Leaderboard ranks players with rank(players, sortBy), which stands in for slow work. It runs on every render, including when you tick Compact, which only changes how the list looks. Make it run only when its inputs change.

Leaderboard takes players, a list of { id, name, score }, and rank, the function it ranks with, rankPlayers by default. The tests pass a spy wrapped around rankPlayers to count calls, so always call rank. It shows a button that switches the order (Sort by name while sorted by score, the starting order, and Sort by score while sorted by name), a checkbox labelled Compact, and an ordered list with one <li> per player, like Chen: 140.

What to fix: ticking Compact doesn't call rank. Switching the order calls it once, and so does a new players list. Rendering Leaderboard again with the same props doesn't call it. rankPlayers must keep returning a new array and leave the one it gets alone. The tests render without Strict Mode.

Write this (App.jsx)
import { useState } from "react";
 
export function rankPlayers(players, sortBy) {
  return [...players].sort((a, b) => (sortBy === "name" ? a.name.localeCompare(b.name) : b.score - a.score));
}
 
export function Leaderboard({ players, rank = rankPlayers }) {
  const [sortBy, setSortBy] = useState("score");
  const [compact, setCompact] = useState(false);
 
  const ranked = rank(players, sortBy);
 
  return (
    <div>
      <button onClick={() => setSortBy(sortBy === "score" ? "name" : "score")}>
        Sort by {sortBy === "score" ? "name" : "score"}
      </button>
      <label>
        <input type="checkbox" checked={compact} onChange={(event) => setCompact(event.target.checked)} /> Compact
      </label>
      <ol className={compact ? "compact" : undefined}>
        {ranked.map((player) => (
          <li key={player.id}>
            {player.name}: {player.score}
          </li>
        ))}
      </ol>
    </div>
  );
}
 
const PLAYERS = [
  { id: 1, name: "Asha", score: 120 },
  { id: 2, name: "Ben", score: 95 },
  { id: 3, name: "Chen", score: 140 },
];
 
export default function App() {
  return <Leaderboard players={PLAYERS} />;
}
The checks your code must pass
  • ranks by score at first, calling rank once
  • switching the order ranks again, once each time
  • ticking Compact doesn't rank again
  • rendering again with the same props doesn't rank again
  • a new players list ranks again
  • rankPlayers leaves the list it gets alone

Try it in the editor

The answer

Write it yourself first. Then compare it with a reference solution and the checks it passes.

Checking your sign-in…