Yes, I think you misunderstand it. Cairo is stateless, it does not remember what has been drawn. Note that there are differences in how Cairo is used in GTK4 and GTK3. In GTK3 we use a signal (I think draw) and in GTK4 we use setDrawFunction() to set the callback which redraws the whole scene. You may find still examples for that for your favorite programming language. For Nim, my tiny toy chess has an example for GTK4 and GTK3, see salewski-chess/board_gtk4.nim at main · StefanSalewski/salewski-chess · GitHub. For other languages ask Google – I think in 2007 I started with a Z-Code C example, maybe you can still find it. Another example with animation would be gintro/cairo_anim.nim at master · StefanSalewski/gintro · GitHub. That is with a timer, the original was from the Cairo mailing list eight years ago, see [cairo] Cairo + GTK animation - high Xorg load
(1) you implement all the draw/paint operations in the event handler for the “draw” signal
(2) this signal is emitted by the system if needed (e.g. by resizing the window)
(3) you can enforce emitting explicitly (gtk_widget_queue_draw)
The “draw” signal handler must be capable to draw/paint everything you need. But if you have complicated scenes of different kind, in a particular case, it would check flags and conditions and draw/paint only elements/parts needed this time. All this happens inside the callback function for the “draw” signal.
In general, this is the approach used in (all?) other GUI systems, e.g. in Windows API.