The composable architecture intentionally does not allow you to mutate app state in effects. The purpose of effects are to handle the "edges" of your program where you don't have control over the results, for example, disk writes, network calls, user input, etc. Effects are designed to handle these sort of fail-able events and hand concrete actions back to your reducer to manage the results. You then update your state based on the actions returned from your effects.
So yes, you are supposed to provide actions with the sole purpose of mutating state as you did in your last snippet.
Ok, but the original example in the repository mutates the state in every action inside the reducer!?
Because it is a simple example only one action uses an environment with a non-async endpoint uuid(). I simply modified this to async, which leads to my question: How do we update state out of an async context?
The modified Todo app with the additional state mutating action runs just fine, but I was hoping that there is a simpler way (like without concurrency). Imagine a large application with a database with multiple tables inside the environment, all database endpoints async... That could be a ton of additional actions, which are not necessary without TCA's concurrency.
Ah, it was not clear to me that you had pulled the example from the TCA repo. I had missed the part in parenthesis were you explicitly said that .
Modifying state in the reducer is the way it was designed. The reducer returns two things: a new state and an effect. The effect is returned directly with thereturn keyword. The state is modified in place using inout. The end result is, in essence, the same as returning the tuple (State, Effect) from the reducer. Just don't modify the state in the effect that is returned from the reducer as that won't execute until later and your state mutations will get out of sync.
Sorry, it it clear to me that all examples are up-to-date. I just modified it as an illustration to my question. I would not use async here
I'm working on a larger app with a database environment where I found a lot of code which, after converting to async, needed additional actions. So may be I have to restrict myself to use async/await here.