Skip to content

Commit

Permalink
refactor(core): avoid passing events to dispatch, to enforce using …
Browse files Browse the repository at this point in the history
…them as side-effects only
  • Loading branch information
sassanh committed Oct 22, 2024
1 parent a3ea8ce commit f06d44c
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 30 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Upcoming

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

## Version 0.18.0

Expand Down
2 changes: 1 addition & 1 deletion redux/basic_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ def __call__(
) -> Callable[[], None]: ...


DispatchParameters: TypeAlias = Action | Event | list[Action | Event]
DispatchParameters: TypeAlias = Action | list[Action]


class Dispatch(Protocol, Generic[State, Action, Event]):
Expand Down
35 changes: 25 additions & 10 deletions redux/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,13 @@ def _run_actions(self: Store[State, Action, Event]) -> None:
if is_complete_reducer_result(result):
self._state = result.state
self._call_listeners(self._state)
self.dispatch([*(result.actions or []), *(result.events or [])])
self._dispatch([*(result.actions or []), *(result.events or [])])
elif is_state_reducer_result(result):
self._state = result
self._call_listeners(self._state)

if isinstance(action, FinishAction):
self.dispatch(cast(Event, FinishEvent()))
self._dispatch([cast(Event, FinishEvent())])

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

@overload
def dispatch(
self: Store[State, Action, Event],
*parameters: DispatchParameters[Action, Event],
with_state: Callable[[State | None], DispatchParameters[Action, Event]]
| None = None,
*parameters: DispatchParameters[Action],
) -> None: ...
@overload
def dispatch(
self: Store[State, Action, Event],
*,
with_state: Callable[[State | None], DispatchParameters[Action]] | None = None,
) -> None: ...
def dispatch(
self: Store[State, Action, Event],
*parameters: DispatchParameters[Action],
with_state: Callable[[State | None], DispatchParameters[Action]] | None = None,
) -> None:
"""Dispatch actions and/or events."""
"""Dispatch actions."""
if with_state is not None:
self.dispatch(with_state(self._state))

items = [
item
for items in parameters
for item in (items if isinstance(items, list) else [items])
actions = [
action
for actions in parameters
for action in (actions if isinstance(actions, list) else [actions])
]
self._dispatch(actions)

def _dispatch(
self: Store[State, Action, Event],
items: list[Action | Event],
) -> None:
for item in items:
if isinstance(item, BaseAction):
action = cast(Action, item)
Expand Down
31 changes: 14 additions & 17 deletions tests/test_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
FinishEvent,
InitAction,
InitializationActionError,
ReducerResult,
)
from redux.main import Store

Expand All @@ -39,14 +40,17 @@ class SomeEvent(BaseEvent): ...
def reducer(
state: StateType | None,
action: Action,
) -> StateType | CompleteReducerResult[StateType, Action, SomeEvent | FinishEvent]:
) -> StateType | ReducerResult[StateType, Action, SomeEvent | FinishEvent]:
if state is None:
if isinstance(action, InitAction):
return StateType(value=0)
raise InitializationActionError(action)

if isinstance(action, IncrementAction):
return replace(state, value=state.value + 1)
return CompleteReducerResult(
state=replace(state, value=state.value + 1),
events=[SomeEvent()],
)

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

store.register_event_middleware(middleware)

events = [
SomeEvent(),
SomeEvent(),
SomeEvent(),
]
actions = [IncrementAction()] * 3

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

store.subscribe_event(FinishEvent, check)

for event in events:
store.dispatch(event)
for action in actions:
store.dispatch(action)


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

store.register_event_middleware(middleware)

events = [
SomeEvent(),
SomeEvent(),
]
actions = [IncrementAction()] * 2

def check() -> None:
assert side_effect_calls == events[1:2]
assert side_effect_calls == actions[1:2]

store.subscribe_event(SomeEvent, some_side_effect)
store.subscribe_event(FinishEvent, check)

for event in events:
store.dispatch(event)
for action in actions:
store.dispatch(action)
store.dispatch(FinishAction())
4 changes: 2 additions & 2 deletions tests/test_weakref.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ def event_subscription_without_keep_ref(_: DummyEvent) -> None:
del event_subscription_without_keep_ref
assert ref2() is None

store.dispatch(DummyEvent())
store._dispatch([DummyEvent()]) # noqa: SLF001

@wait_for
def subscriptions_ran() -> None:
Expand Down Expand Up @@ -343,7 +343,7 @@ def test_event_subscription_method(
del instance_without_keep_ref
assert ref() is None

store.dispatch(DummyEvent())
store._dispatch([DummyEvent()]) # noqa: SLF001

@wait_for
def subscriptions_ran() -> None:
Expand Down

0 comments on commit f06d44c

Please sign in to comment.