Skip to content

Commit f06d44c

Browse files
committed
refactor(core): avoid passing events to dispatch, to enforce using them as side-effects only
1 parent a3ea8ce commit f06d44c

File tree

5 files changed

+43
-30
lines changed

5 files changed

+43
-30
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Upcoming
44

55
- chore: migrate from poetry to uv for the sake of improving performance and dealing with conflicting sub-dependencies
6+
- refactor(core): avoid passing events to `dispatch`, to enforce using them as side-effects only
67

78
## Version 0.18.0
89

redux/basic_types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ def __call__(
282282
) -> Callable[[], None]: ...
283283

284284

285-
DispatchParameters: TypeAlias = Action | Event | list[Action | Event]
285+
DispatchParameters: TypeAlias = Action | list[Action]
286286

287287

288288
class Dispatch(Protocol, Generic[State, Action, Event]):

redux/main.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,13 @@ def _run_actions(self: Store[State, Action, Event]) -> None:
136136
if is_complete_reducer_result(result):
137137
self._state = result.state
138138
self._call_listeners(self._state)
139-
self.dispatch([*(result.actions or []), *(result.events or [])])
139+
self._dispatch([*(result.actions or []), *(result.events or [])])
140140
elif is_state_reducer_result(result):
141141
self._state = result
142142
self._call_listeners(self._state)
143143

144144
if isinstance(action, FinishAction):
145-
self.dispatch(cast(Event, FinishEvent()))
145+
self._dispatch([cast(Event, FinishEvent())])
146146

147147
def _run_event_handlers(self: Store[State, Action, Event]) -> None:
148148
while len(self._events) > 0:
@@ -177,22 +177,37 @@ def wait_for_event_handlers(self: Store[State, Action, Event]) -> None:
177177
"""Wait for the event handlers to finish."""
178178
self._event_handlers_queue.join()
179179

180+
@overload
180181
def dispatch(
181182
self: Store[State, Action, Event],
182-
*parameters: DispatchParameters[Action, Event],
183-
with_state: Callable[[State | None], DispatchParameters[Action, Event]]
184-
| None = None,
183+
*parameters: DispatchParameters[Action],
184+
) -> None: ...
185+
@overload
186+
def dispatch(
187+
self: Store[State, Action, Event],
188+
*,
189+
with_state: Callable[[State | None], DispatchParameters[Action]] | None = None,
190+
) -> None: ...
191+
def dispatch(
192+
self: Store[State, Action, Event],
193+
*parameters: DispatchParameters[Action],
194+
with_state: Callable[[State | None], DispatchParameters[Action]] | None = None,
185195
) -> None:
186-
"""Dispatch actions and/or events."""
196+
"""Dispatch actions."""
187197
if with_state is not None:
188198
self.dispatch(with_state(self._state))
189199

190-
items = [
191-
item
192-
for items in parameters
193-
for item in (items if isinstance(items, list) else [items])
200+
actions = [
201+
action
202+
for actions in parameters
203+
for action in (actions if isinstance(actions, list) else [actions])
194204
]
205+
self._dispatch(actions)
195206

207+
def _dispatch(
208+
self: Store[State, Action, Event],
209+
items: list[Action | Event],
210+
) -> None:
196211
for item in items:
197212
if isinstance(item, BaseAction):
198213
action = cast(Action, item)

tests/test_middleware.py

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
FinishEvent,
1717
InitAction,
1818
InitializationActionError,
19+
ReducerResult,
1920
)
2021
from redux.main import Store
2122

@@ -39,14 +40,17 @@ class SomeEvent(BaseEvent): ...
3940
def reducer(
4041
state: StateType | None,
4142
action: Action,
42-
) -> StateType | CompleteReducerResult[StateType, Action, SomeEvent | FinishEvent]:
43+
) -> StateType | ReducerResult[StateType, Action, SomeEvent | FinishEvent]:
4344
if state is None:
4445
if isinstance(action, InitAction):
4546
return StateType(value=0)
4647
raise InitializationActionError(action)
4748

4849
if isinstance(action, IncrementAction):
49-
return replace(state, value=state.value + 1)
50+
return CompleteReducerResult(
51+
state=replace(state, value=state.value + 1),
52+
events=[SomeEvent()],
53+
)
5054

5155
if isinstance(action, DecrementAction):
5256
return replace(state, value=state.value - 1)
@@ -131,19 +135,15 @@ def middleware(event: SomeEvent) -> SomeEvent | FinishEvent:
131135

132136
store.register_event_middleware(middleware)
133137

134-
events = [
135-
SomeEvent(),
136-
SomeEvent(),
137-
SomeEvent(),
138-
]
138+
actions = [IncrementAction()] * 3
139139

140140
def check() -> None:
141-
assert calls == events
141+
assert calls == [SomeEvent()] * 3
142142

143143
store.subscribe_event(FinishEvent, check)
144144

145-
for event in events:
146-
store.dispatch(event)
145+
for action in actions:
146+
store.dispatch(action)
147147

148148

149149
def test_cancelling_event_middlewares(store: StoreType) -> None:
@@ -162,17 +162,14 @@ def some_side_effect(event: SomeEvent) -> None:
162162

163163
store.register_event_middleware(middleware)
164164

165-
events = [
166-
SomeEvent(),
167-
SomeEvent(),
168-
]
165+
actions = [IncrementAction()] * 2
169166

170167
def check() -> None:
171-
assert side_effect_calls == events[1:2]
168+
assert side_effect_calls == actions[1:2]
172169

173170
store.subscribe_event(SomeEvent, some_side_effect)
174171
store.subscribe_event(FinishEvent, check)
175172

176-
for event in events:
177-
store.dispatch(event)
173+
for action in actions:
174+
store.dispatch(action)
178175
store.dispatch(FinishAction())

tests/test_weakref.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ def event_subscription_without_keep_ref(_: DummyEvent) -> None:
300300
del event_subscription_without_keep_ref
301301
assert ref2() is None
302302

303-
store.dispatch(DummyEvent())
303+
store._dispatch([DummyEvent()]) # noqa: SLF001
304304

305305
@wait_for
306306
def subscriptions_ran() -> None:
@@ -343,7 +343,7 @@ def test_event_subscription_method(
343343
del instance_without_keep_ref
344344
assert ref() is None
345345

346-
store.dispatch(DummyEvent())
346+
store._dispatch([DummyEvent()]) # noqa: SLF001
347347

348348
@wait_for
349349
def subscriptions_ran() -> None:

0 commit comments

Comments
 (0)