To make complex layouts (flex, ...) and have them run performantly you need some form of a retained backing state anyways.
The main difference between various retained, OOP, functional, immediate, declarative, ..., approaches is how they treat this state.
For imperative/retained/OOP libraries, you operate on this state itself. Your nodes/widgets know their own state, how to render themselves, their place in the hierarchy and so on.
For immediate/functional(?) libraries, this state is a cache. It's not something you work with directly.
Despite not really liking React itself, I think it has found the best model.
You take a retained core, possibly OOP, maybe ECS or whatever, and you write a declarative wrapper around it. This lets you escape the easy-mode declarative landscape when needed, but most UI can still be simple to write.
The main difference between various retained, OOP, functional, immediate, declarative, ..., approaches is how they treat this state.
For imperative/retained/OOP libraries, you operate on this state itself. Your nodes/widgets know their own state, how to render themselves, their place in the hierarchy and so on.
For immediate/functional(?) libraries, this state is a cache. It's not something you work with directly.
Despite not really liking React itself, I think it has found the best model.
You take a retained core, possibly OOP, maybe ECS or whatever, and you write a declarative wrapper around it. This lets you escape the easy-mode declarative landscape when needed, but most UI can still be simple to write.