I don't think it's "bad practice" as long as it works fine for you. One think you should consider, however, is that you might be omitting one of the strengths of NgRx: Memoization. Selectors are an insanely powerful feature in NgRx that truely boost your performance to new heights and you should use them to their absolut limit.
I assume, that your viewmodels contain redundant data that you update whenever new data comes from the users inputs or your backend service. So your state is (1) unnecessarily large and (2) drains additional performance when building your viewmodels.
So, instead of building redundant viewmodels, try to expose your viewmodels as NgRx selectors that compose all information a view needs. This allows your selectors to fully take advantage of their memoization capabilities which will result in a lot less change detection cycles. Another approach, that I personally prefer the most, is that your views (i.e. components) select all the slices from state they need. This might result in a list of queries from different feature modules in your views, but it allows to fully decouple your state. This is especially useful when you work in larger teams where you have some people working exclusively on the view and others working and designing exclusively the state.
The people working on the view just have to query what they need and you, who is working on the state, does not have to consider building any viewmodels that require knowledge of the view that is being built later on.
Last but not least, querying smaller portions of your state also results in less change detection cycles because your async pipes don't have to trigger for every tiny change in your viewmodel but instead only update a small part (maybe only a single element) in your DOM instead of your entire component.
So, to conclude my statement, you get 5 advantages from NOT using viewmodels:
- Smaller state since redundant data is eliminated
- Better performance because you don't have to build viewmodels in the first place
- Decoupling your state from your view which leads to more scalable, testable solutions that have improved separation of concerns
- Better performance through selector memoization
- Better performance through atomic UI updates
For reference: