You’re absolutely right, ‘expose on spawn’ is a way to get around this when you’re working in blueprint.
However, I’ll point out that this has the limitation that it requires that you have a class member that can be directly initialized. Also any construction parameters that might be transformed (and not directly stored anywhere) or any work that you might want to perform before BeginPlay is called still don’t fit well into that process.
In fact, I had this problem regularly enough within my UI code that I wrote a custom Blueprint node that finds and calls an Init function (optionally of course) and exposes the input parameters to Init on the node in the same way as the “expose on spawn” parameters. I use this custom node for allocating widgets over the engine version almost exclusively.