The second argument decides how often React 7,897 re-synchronizes. React compares each item with its value from the previous render using Object.is; if every item is equal, the setup function is skipped.
| Second argument | When the setup function runs |
|---|---|
| omitted | After every render, including every state update |
| [roomId] or [a, b] | After the first render, and after any render where a listed value changed |
| [] | Once when the component mounts (twice in development, see Cleanup) |
The array is not a choice — it is a fact about the code. Every reactive value the setup function reads (props, state, and anything computed from them during render) must appear in it. Omitting one does not "run the effect less"; it runs the effect with values frozen from an earlier render.
function ChatRoom({ roomId, theme }) { // theme is not read below
const [messages, setMessages] = useState([]);
useEffect(() => {
const conn = createConnection(roomId);
conn.on('message', m => setMessages(prev => [...prev, m]));
conn.connect();
return () => conn.disconnect();
}, [roomId]); // setMessages is stable, so it is not listed
return <MessageList items={messages} />;
}Let a linter check this instead of your memory. npm 2,036 i -D eslint-plugin-react-hooks@7.1.1 enables the react-hooks/exhaustive-deps rule, which reports the missing or superfluous entry and offers a fix.