Dependencies

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.

How the dependency array controls an effect
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.

A dependency array that matches what the effect readsJavaScript
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.