Fetching Data & APIs

⚛️ ReactLesson 14Advanced

Most React apps communicate with REST APIs. In this lesson we cover the full data-fetching lifecycle: loading states, error handling, caching, mutations, and optimistic UI updates.

1 Fetch with Loading & Error States
React — Complete Fetch Pattern
function PostList() {
  const [posts,   setPosts]   = useState([]);
  const [loading, setLoading] = useState(true);
  const [error,   setError]   = useState(null);

  useEffect(() => {
    const controller = new AbortController();

    fetch("https://jsonplaceholder.typicode.com/posts", {
      signal: controller.signal
    })
      .then(res => {
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        return res.json();
      })
      .then(data => { setPosts(data.slice(0, 10)); setLoading(false); })
      .catch(err => {
        if (err.name !== "AbortError") { setError(err.message); setLoading(false); }
      });

    return () => controller.abort(); // cancel on unmount
  }, []);

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

  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}><strong>{post.title}</strong></li>
      ))}
    </ul>
  );
}
2 POST / Mutations
React — POST Request
async function createPost(data) {
  const res = await fetch("https://jsonplaceholder.typicode.com/posts", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(data),
  });
  if (!res.ok) throw new Error("Failed to create post");
  return res.json();
}

function NewPostForm() {
  const [title, setTitle]     = useState("");
  const [saving, setSaving]   = useState(false);

  const handleSubmit = async (e) => {
    e.preventDefault();
    setSaving(true);
    try {
      const post = await createPost({ title, userId: 1 });
      console.log("Created:", post);
    } catch (err) {
      console.error(err);
    } finally {
      setSaving(false);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input value={title} onChange={e => setTitle(e.target.value)} />
      <button disabled={saving}>{saving ? "Saving..." : "Create Post"}</button>
    </form>
  );
}
3 Code Challenge
Challenge: Build a CRUD todo app using https://jsonplaceholder.typicode.com/todos. Fetch 10 todos on mount, allow marking complete (PUT), and deleting (DELETE). Show loading spinners per operation.