🚫 𝗦𝘁𝗼𝗽 𝗗𝗲𝗯𝘂𝗴𝗴𝗶𝗻𝗴 𝗥𝗲𝗻𝗱𝗲𝗿𝘀 𝗪𝗶𝘁𝗵 𝗖𝗼𝗻𝘀𝗼𝗹𝗲 𝗟𝗼𝗴𝘀 I once saw a junior dev do this for hours. The problem wasn't the state; it was 𝘸𝘩𝘦𝘯 the state updated. They were logging state in the render body, seeing the "correct" value, but the UI was always one step behind. Their bug wasn't the 𝘷𝘢𝘭𝘶𝘦—it was a stale closure in a 𝘂𝘀𝗲𝗘𝗳𝗳𝗲𝗰𝘁 that was committing an old value, completely out of sync with the new render's log. They were debugging the render phase, but the bug was in the commit phase. To master React, you must internalize its render cycle. It's a distinct three-phase process: • 𝗥𝗲𝗻𝗱𝗲𝗿 𝗣𝗵𝗮𝘀𝗲: React calls your component functions to get an in-memory snapshot (React elements) of what the UI should be. This is a pure calculation; no DOM is touched. • 𝗥𝗲𝗰𝗼𝗻𝗰𝗶𝗹𝗶𝗮𝘁𝗶𝗼𝗻: React diffs this new snapshot (the "virtual DOM") against the previous one to find the minimal set of changes needed. • 𝗖𝗼𝗺𝗺𝗶𝘁 𝗣𝗵𝗮𝘀𝗲: React takes those calculated changes and mutates the real browser DOM. Only after this commit do your useEffect hooks run. State/Prop Change -> [ 𝗥𝗲𝗻𝗱𝗲𝗿 𝗣𝗵𝗮𝘀𝗲 ] (Pure: Calculates UI) -> [ 𝗥𝗲𝗰𝗼𝗻𝗰𝗶𝗹𝗶𝗮𝘁𝗶𝗼𝗻 ] (Diffs virtual tree) -> [ 𝗖𝗼𝗺𝗺𝗶𝘁 𝗣𝗵𝗮𝘀𝗲 ] (Mutates DOM, runs effects) This cycle only runs when triggered: • A state setter (useState, useReducer) is called. • Props from a parent component change. • A subscribed context value changes. • (In Dev) React Strict Mode double-invokes renders. • A transition marks an update as non-urgent. Watch for these common cycle-related traps: • 𝗠𝘂𝘁𝗮𝘁𝗶𝗼𝗻𝘀 𝗱𝘂𝗿𝗶𝗻𝗴 𝗿𝗲𝗻𝗱𝗲𝗿: Never change state or refs in the render body. • 𝗦𝘁𝗮𝗹𝗲 𝗱𝗲𝗿𝗶𝘃𝗲𝗱 𝘀𝘁𝗮𝘁𝗲: Using props to initialize useState. • 𝗘𝗳𝗳𝗲𝗰𝘁 𝗹𝗼𝗼𝗽𝘀: useEffect updates state, which re-triggers the same effect. • 𝗨𝗻𝘀𝘁𝗮𝗯𝗹𝗲 𝗿𝗲𝗳𝗲𝗿𝗲𝗻𝗰𝗲𝘀: New functions/objects in render (e.g., onClick={() => {}}) causing child re-renders. #reactjs #javascript #webdevelopment #frontend #performance
Great breakdown! Understanding React’s commit phase is a game-changer — most bugs come from effects running out of sync with renders
Effects shouldn't ideally be used for updating the states except synchronising the components with external resources.(That way, cyclic issue of state updates will never happen) In most cases effects are not even needed. I have observed most of the devs doing: Event-->update state in parent --> runEffect in child components. The last part is redundant, and a very bad practice.