awesome-everything RU
↑ Back to the climb

Frontend Architecture

State shape: code reading

Crux Read real React/TS snippets, predict the shape bug, and pick the highest-leverage fix — derived state, normalized vs nested, and colocation.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at senior altitude — in orbit
◷ 14 min

Shape bugs hide in plain code that compiles and renders fine until an update path it forgot to cover fires. Read each snippet, find the second source of truth or the misplaced state, then pick the fix a senior makes first.

Goal

Practise the loop you run when reviewing state code: read the component, spot the value that should be derived or the data shaped wrong for its access pattern, and reach for the structural fix before any library.

Snippet 1 — the stored derived value

function Cart({ items }: { items: Item[] }) {
  const [total, setTotal] = useState(0);

  useEffect(() => {
    setTotal(items.reduce((s, i) => s + i.price * i.qty, 0));
  }, [items]);

  return <Badge>{total}</Badge>;
}
Quiz

What is the shape bug here, and the senior fix?

Snippet 2 — nested vs normalized

// state.users[].posts[] — users embedded inside each post's author field too
function renameUser(state, id: string, name: string) {
  return {
    ...state,
    users: state.users.map(u => u.id === id ? { ...u, name } : u),
    // posts still carry post.author = {...the old user...}
  };
}
Quiz

A rename updates the users array but the post list still shows the old name. What shape change is the real fix?

Snippet 3 — the hoisted, over-broad state

// app store
const useStore = create((set) => ({
  mouseX: 0, mouseY: 0,
  setMouse: (x, y) => set({ mouseX: x, mouseY: y }),
}));

function Dashboard() {
  const { mouseX, mouseY } = useStore();   // subscribes to the whole store
  return <ExpensiveCharts /* re-renders on every mousemove */ />;
}
Quiz

Why does scrolling the mouse tank this dashboard's frame rate, and what is the fix?

Snippet 4 — server data as client state

function Orders() {
  const [orders, setOrders] = useState<Order[]>([]);
  useEffect(() => { fetch('/api/orders').then(r => r.json()).then(setOrders); }, []);
  // also rendered in a sibling <OrdersSummary/> with its own identical fetch
}
Quiz

Two components run this same fetch-into-useState pattern. Which set of problems does the shape create, and what is the fix?

Recap

Every shape bug above is read straight from code: an effect syncing a value you can derive (compute in render instead), authors embedded in posts so a rename goes stale (normalize to byId + join), high-frequency state hoisted into a global store that re-renders the world (colocate or select a slice), and a fetch dragged into useState that should be server cache (use a cache library keyed by the request). Read the component, find the second source of truth or the state placed wrong for its readers, and fix the shape before reaching for a library.

Continue the climb ↑State shape: refactor the state soup
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.