For the record, there's a truly fascinating design that I've seen implemented.
1. Make class Message Handler as a *by value* object. In other words, it has methods for "Handle Message X" and "Handle Message Y", and all of its data is within itself.
2. Put that class in a shift register of a message handling loop. Dequeue a message, figure out the type of the message, call the right method (how you figure out the message type -- strings, enums, dynamic dispatching, variants, finger in the wind -- doesn't matter).
3. Some messages tell the message handler to "change state". Let's say message X is such a message. In the "Handle Message X" method, instead of changing some internal variable, output an entirely new Message Handler object of a completely different Message Handler child class. That now goes around on the shift register. All future messages will dispatch to the new set of methods. Any relevant data from the old object must be copied/swapped into the new object.
This eliminates the case structure in the message handling of "if I am this state, then do this action" entirely.
Because we made the output terminal NOT be a dynamic dispatch output, you can implement this in the Actor Framework and switch out which Actor is in your loop while the actor is running.
This is known in CS circles as the State Pattern.