Replies: 1 comment 7 replies
-
@Jeehut We actually posted an RFC to simplify the surface area of Notably, we suggest eliminating I also think we want to keep viewStore.send(.action)
return .send(.action)
return .run { send in send(.action) }
await testStore.send(.action) And I think |
Beta Was this translation helpful? Give feedback.
-
I am using TCA for a while now, and I've come across several hurdles, some of which have been fixed with the recent features added. But here's one thing that still makes me scratch my head from time to time even after writing over 100 reducers:
What call is the correct one when I want to return an
Effect
in aReducer
when reacting to anAction
?Many APIs in TCA have been streamlined and made more consistent in preparation of the
1.0
release, such asBindingState
andBindingAction
, orPresentationState
andPresentationAction
, or the consistently named.ifLet
operator for different kinds of child types (sheets, alerts, etc.), or even puttingState
andAction
into a namespace. All these changes helped make developing with TCA more natural and less annoying. I even accepted the (to me) new termReducer
by viewing it as something unrelated to thereduce
method, being its own thing with its own meaning (= a mix between an action handler and a state updater).But the methods we can call inside a reducer to return an effect seem to lack this level of consistency:
.send(Action)
: Immediately emits a single action to the system, the action is passed to it as a parameter..task { ... }
: Executes code in an asynchronous context (= allowsawait
calls), closure must return exactly oneAction
..run { send in ... }
: Executes code in an asynchronous context (like.task
), closure can emit unlimited actions tosend
..fireAndForget { ... }
: Executes code in an asynchronous context (like.task
), closure can't emit any actions..merge(Effect...)
: Starts multiple effects from one action case, running them all at the same time..concatenate(Effect...)
: Starts multiple effects from one action case, running them one after another.Just from reading these descriptions, I immediately think that
task
,run
andfireAndForget
are pretty much doing the same thing but with different number of actions passed back into the system. That's an opportunity to streamline them. Similarly,merge
andconcatenate
also seem to be doing a similar thing with differences in behavior. Another streamlining opportunity.But in the best case, we can even fine one name for all these APIs which only differs in ways that describe their differences. What is it that they all have in common? They all emit a given number of actions (for
fireAndForget
the number simply is zero). So I would suggest that they all get renamed to something like.emit
or.send
or something else that makes sense for sending/emitting actions into the system. I will useemit
for the detailed naming down below to not confuse users with the existingsend
word, but it could be adjusted for any other unified word if you prefer another one.The mapping of the existing methods to the new unified name could look like this:
.send(Action)
becomes.emitOne(Action)
or short.emit(Action)
.task { ... }
becomes.emitOne { ... }
or short.emit { ... }
(or even.emitOneAsync { ... }
).run { send in ... }
becomes.emitMany { send ... }
or short.emit { send ... }
(or even.emitManyAsync { send in ... }
).fireAndForget { ... }
becomes.emitZero { ... }
or short.noEmit { ... }
(or.emitZeroAsync { ... }
?).merge(Effect...)
becomes.emitAllAtOnce(Effect...)
(or.emitAllConcurrently(Effect...)
).concatenate(Effect...)
becomes.emitOneAfterTheOther(Effect...)
(oremitAllSequentially(Effect...)
)For the
merge
andconcatenate
replacements I suggest to add anEffectBuilder
to allow writing like this instead (if possible):I may try to write an extension to
Effect
and anEffectBuilder
that mimics above behavior.But I wanted to share the idea first. What do you think?
Beta Was this translation helpful? Give feedback.
All reactions