React - Optimistic Updates

Overview

Estimated time: 20–30 minutes

Improve perceived performance by applying updates instantly and reconciling with server results.

Try it: Like button with optimistic UI

View source
function fakeServerToggleLike(current){
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() < 0.8) resolve(!current); else reject(new Error('Network error'));
    }, 500);
  });
}
function App(){
  const [liked, setLiked] = React.useState(false);
  const [error, setError] = React.useState('');
  const [pending, setPending] = React.useState(false);
  async function onToggle(){
    setError('');
    const prev = liked; setLiked(!liked); setPending(true);
    try{ const server = await fakeServerToggleLike(prev); setLiked(server); }
    catch(e){ setLiked(prev); setError('Could not save, rolled back.'); }
    finally{ setPending(false); }
  }
  return (
    <div>
      <button onClick={onToggle} disabled={pending}>{liked ? '♥ Liked' : '♡ Like'}{pending? '…':''}</button>
      <div aria-live="polite" style={{color:'crimson'}}>{error}</div>
    </div>
  );
}
ReactDOM.createRoot(document.getElementById('try-optimistic')).render(<App />);

Syntax primer

  • Apply the change on the client immediately; send the request in the background.
  • On failure, roll back to previous state and notify the user.

Common pitfalls

  • Race conditions when multiple updates occur—queue or tag operations to reconcile correctly.

Exercises

  1. Optimistically add a comment to a list; roll back on error.

Checks for Understanding

  1. Why stash previous state before an optimistic update?
  2. How do you handle multiple concurrent optimistic updates safely?
Show answers
  1. So you can roll back precisely if the server request fails.
  2. Tag updates with IDs or queue them; reconcile responses with the right update.