React - Calling APIs & Actions
Overview
Estimated time: 20–30 minutes
Call APIs based on user actions, show loading and error states, and cancel in-flight requests to keep the UI responsive.
Try it: Load Posts on Button Click
View source
const { useState, useRef } = React;
function Posts(){
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');
  const controllerRef = useRef(null);
  async function load(){
    try {
      setError(''); setLoading(true);
      if (controllerRef.current) controllerRef.current.abort();
      controllerRef.current = new AbortController();
      const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5', { signal: controllerRef.current.signal });
      if (!res.ok) throw new Error('Network error');
      const data = await res.json();
      setPosts(data);
    } catch (e) {
      if (e.name !== 'AbortError') setError(e.message || 'Error');
    } finally {
      setLoading(false);
    }
  }
  function cancel(){ if (controllerRef.current) controllerRef.current.abort(); }
  return (
    <div>
      <button onClick={load} disabled={loading}>{loading ? 'Loading...' : 'Load Posts'}</button>
      <button onClick={cancel} style={{marginLeft:8}} disabled={!loading}>Cancel</button>
      {error && <div style={{color:'salmon', marginTop:8}}>{error}</div>}
      <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>
    </div>
  );
}
ReactDOM.createRoot(document.getElementById('try-api')).render(<Posts />);
Syntax primer
- fetch(url)returns a Promise; use- awaitfor readability.
- AbortControllercancels in-flight requests to avoid race conditions.
Common pitfalls
- Forgetting to handle non-OK responses (res.ok).
- Updating state after unmount; cancel or guard against setState on unmounted components.
Exercises
- Load 10 posts and add a dropdown to choose limit.
- Show a retry button when there is an error.
Checks for Understanding
- How do you cancel an in-flight fetch and why would you do it?
- Why should you check res.ok before using the response body?
- What strategies help avoid setState on an unmounted component?
Answers
- Use AbortController and pass its signal to fetch; cancel to prevent race conditions and to avoid updating with stale data.
- Non-OK responses (4xx/5xx) won’t throw by default; checking res.ok lets you create a meaningful error path and show UI errors.
- Abort/cancel on unmount, keep a canceled flag, or guard with isMounted refs; libraries often abstract this for you.