Week 12

TanStack Query

Optimistic updates

Authentication

Data persistance

Security Fundamentals

Practice

Assignment

Frontend Track

Content

Let’s get practical

Exercise 1: Replace a useEffect Fetch with useQuery

Difficulty: Easy Concepts: useQuery, query keys, isPending/isError, automatic caching

Take the component below written with useEffect and useState and refactor it to use useQuery from TanStack Query.

Starting point - paste this into a new component file:

'use client'

import { useEffect, useState } from 'react'

type Post = { id: number; title: string; body: string }

export default function PostList() {
  const [posts, setPosts] = useState<Post[]>([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)

  useEffect(() => {
    fetch('<https://jsonplaceholder.typicode.com/posts?_limit=10>')
      .then(res => {
        if (!res.ok) throw new Error(`HTTP ${res.status}`)
        return res.json()
      })
      .then(data => {
        setPosts(data)
        setLoading(false)
      })
      .catch(err => {
        setError(err.message)
        setLoading(false)
      })
  }, [])

  if (loading) return <p>Loading posts...</p>
  if (error) return <p>Error: {error}</p>

  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.body}</p>
        </li>
      ))}
    </ul>
  )
}

Requirements:

Hints:

Exercise 2: Write Data with useMutation

Difficulty: Easy Concepts: useMutation, mutationFn, useQueryClient, invalidateQueries

Add a form that creates a new post and invalidates the cached list so the UI updates without a manual refetch.

Requirements:

Hints:

Exercise 3: Optimistic Toggle with Rollback

Difficulty: Medium Concepts: useMutation lifecycle (onMutate, onError, onSettled), cache snapshot, cancelQueries

Build a list of todos with a checkbox on each one. Toggling the checkbox should feel instant, and the UI must roll back cleanly if the server rejects the change.

Starting point - use this query as the data source:

type Todo = { id: number; userId: number; title: string; completed: boolean }

const { data: todos } = useQuery<Todo[]>({
  queryKey: ['todos'],
  queryFn: () =>
    fetch('<https://jsonplaceholder.typicode.com/todos?_limit=8>').then(r => r.json()),
})

Requirements:

Hints:

Exercise 4: Storage and Protected Routes

Difficulty: Easy Concepts: localStorage, JWT structure (mock), protected routes, useEffect for client-only APIs

Build a hardcoded login flow that stores a fake token in localStorage and gates a dashboard page behind it.

Requirements:

Hints:


The HackYourFuture curriculum is licensed under CC BY-NC-SA 4.0 *https://hackyourfuture.net/*

CC BY-NC-SA 4.0 Icons

Built with ❤️ by the HackYourFuture community · Thank you, contributors

Found a mistake or have a suggestion? Let us know in the feedback form.