Replies: 3 comments 8 replies
-
Hey @crayment, this is a very good suggestion! I just opened a PR with this functionality: #1620 |
Beta Was this translation helpful? Give feedback.
-
Personally, I feel like having hardcoded use of dependencies inside your state structs is causing strong coupling and is unnecessary. I think you should just be passing in the values you need to the initialiser. I don't see how this creates a cascading parent dependency injection issue, because the reducer that initialises the state can still use
To me this is much clearer than having hidden dependencies inside the initialisers of your value types. It also makes initialising your state with a specific value in your tests trivial without having to jump through hoops to configure your dependencies. |
Beta Was this translation helpful? Give feedback.
-
I may need to see more code to fully understand, but it seems even if you have multiple levels of state to create that you would be able to do something like this from the parent domain: case .buttonTapped:
state.child = Child.State(
grandchild: GrandChild.State(
date: self.date.now
)
) That is, no need to pass dependencies down and instead just invoke the dependencies from the parent to create the child states. |
Beta Was this translation helpful? Give feedback.
-
Describing the problem
Working on adopting ReducerProtocol and have a question about a pattern we have been using. We often use the current environment in our state initializers. Something like this:
This works fine but becomes a bit frustrating when testing since we need to control dependencies before the store is created
And there is a similar issue when sending actions to the test store. Say there is a parent feature that optionally presents the one from above:
When we try to test the parent's action that creates the child state we run into the same issue:
Alternatives & Reasoning
You may be tempted to simply wait until a
task
oronAppear
effect fires to setup this state. But this isn't ideal because the view needs to render once with a default value, fire its event, then immediately update with the new value. We have seen display glitches when forcing the view to update so quickly after being created, and it's also a bit wasteful. It also forces you to have a default value or make the state optional. Neither of which are ideal.Another alternative would be to simply pass the date into the
init
. This pushes the responsibility to the parent and comes with the problem of dependency injection cascading many levels of parent hierarchy unnecessarily.You could also have two versions of the
init
. One that is used by parent reducers and another that is used by the tests. Then the tests would be able to specify the exact value they want. This is a bit annoying because Swift doesn't generate a default initializer when you implement one like I have above. So you have to implement a whole newinit
just for test code.Proposed Solution
I would love to get thoughts on making the
updateExpectingResult
argument onTestStore.send
be called with the dependencies from the store. As well I think you could makeTestStore.init
use@autoclosure
for theinitialState
argument and accept eitherDependencyValues
directly, or a closure for configuring them.I have solved this for myself with the following extensions which allow dependencies to be controlled for both the
TestStore.init
andtestStore.send
state update.Usage. Tests now both pass!
Beta Was this translation helpful? Give feedback.
All reactions