Skip to content

Update useStoreSelector to pass (current, prev?) to selectors#21

Open
markerikson wants to merge 6 commits into
thejustinwalsh:mainfrom
markerikson:feature/selector-prev-arg
Open

Update useStoreSelector to pass (current, prev?) to selectors#21
markerikson wants to merge 6 commits into
thejustinwalsh:mainfrom
markerikson:feature/selector-prev-arg

Conversation

@markerikson
Copy link
Copy Markdown
Contributor

@markerikson markerikson commented Dec 22, 2025

Stacked on top of #20 for TS improvements.

This PR:

  • Updates useStoreSelector to now pass (current, prev?) to the provided selector.

Background

Per discussion: Jordan didn't want to build equality function support into useStoreSelector directly. Instead, he wants to pass (current, prev) to selectors. That ought to enable building equality function support on top, but also will enable something for Relay as well.

Notes

The new tests are entirely vibe-coded by Claude Opus. I've reviewed them and they look entirely plausible, and currently all pass.

@markerikson markerikson changed the title Fix exported type errors Update useStoreSelector to pass (current, prev?) to selectors Dec 22, 2025
@captbaritone
Copy link
Copy Markdown
Collaborator

This is one I've been stuck on previously. In the sync world current/prev have pretty obvious definitions. In concurrent mode I think we (I) need to ensure I have clarity on what that actually means.

There are a number of different situations we might be in at any given moment:

  • No transition pending
  • Transition pending
  • Transition pending interrupted by a sync update

This means there are a number different states we could have access to:

  • Head: The state that is the result of applying all dispatched actions in order
  • Synthetic: A non-canonical state synthesized by React if a sync update is dispatched while a separate update is still pending as part of a transition.
  • Committed: The state currently rendering in the DOM. During a transition this may be stale or synthetic.

And a number of different parts of the lifecycle that we might call a selector:

  • Initial mount
  • Change to selector/store (semantically the same as mount I think?)
  • Scheduling a fixup due to update between render and mount
  • Scheduling a fixup due to pending transition during sync mount
  • Updating sync
  • Updating as part of a transition

Before adding this I feel like I need to be sure that we are passing the correct (current/prev) pair for each of the combinations of these situations/lifecycles. If we get that wrong we could either trigger a rerender when one is not strictly necessary or (worse) prevent a render when one is actually needed.

I need to build some mental model/abstraction so I can be sure we are getting this right in all cases. Do you feel like you arrived at that while writing this PR?

@markerikson
Copy link
Copy Markdown
Contributor Author

Unfortunately no :) I got some form of this working in this PR, but I think you've got a much better grasp of the various scenarios and possibilities here, and getting that mental model right is going to be the biggest key here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants