-
Notifications
You must be signed in to change notification settings - Fork 48.4k
Remove useEffect from useSyncExternalStoreWithSelector #22977
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Hey, I wrote those release notes :) This change looks kinda questionable to me. It's changing the behavior of the non-shim version of The main reason I even brought up perf in the first place was that React-Redux v6 tried to pass down state updates via context inside of FWIW
I am curious, what did you mean by this? |
In general writing to a ref and reading from that ref in a single Also the failure might be because this line would be wrong after this change. |
@salazarm I now this is "hacky", but in this case we are dealing with "memoizedSelector" function, which is a pure function that returns a value, and the mutable "inst" object only exists to hold the current value and compare it to the next call. So this changed logic should not cause problems in concurrent mode. And I can't imagine a case where the selector is impure... @markerikson I was using old versions of react-redux based on context, understand you. As for the stability of selectors, the initial API (invented by @acdlite I think) was a better idea for me. |
@ku8ar I feel like you're kinda mixing up a few different things here. First, if all users of React-Redux and Zustand suddenly had to make their selectors stable, this would be a huge breaking change for their codebases. Every usage like After we discussed that issue with Andrew, he determined that Second, arrow functions have nothing to do with selectors in and of themselves. You can write selectors with the |
@markerikson You didn't quite understand me. This is a hook with selector currently suggested by your doc: const counter = useSelector(state => state.counter.value) And this is a hook with a selector without a dynamically generated arrow function (stable, which I wrote about earlier). const selectCounterValue = state => state.counter.value
// ...
const counter = useSelector(selectCounterValue) Using this convention, most selectors are stable. Of course, for many ugly projects, this would require a refactoring. But by cutting away from unstable selectors, react-redux would become something of a framework whose requirements suggest writing beatufiul code (stable, pure and named functions). But unfortunately backwards compatibility won. OK, I guess there's no point in arguing further. Happy holidays ;) |
Yes, because we don't want to break user code without absolutely good reason. That's part of the responsibility of maintaining a widely used project. "I think this code looks better if you extract the functions" is not a good reason. |
Summary
The motivation for this PR is the following comment:
https://github.com/reduxjs/react-redux/releases/tag/v8.0.0-alpha.0
Until now, I thought that useMutableSource/useSyncExternalStore would be a much more efficient solution than the current useSelector from react-redux because it would use Fiber's internal algorithm instead of using multiple hooks like useLayoutEffect, useMemo, useState, etc.
But I saw this comment, then read the whole discussion about selector stability (which I thought went the wrong way) and realized that a useSelector based on useSyncExternalStore would have the same performance.
So to optimize the code a bit, I removed the useEffect. Smaller Fiber, less phases.
The code isn't pretty, but it's also not much bigger than before, so it shouldn't make it harder to read.
How did you test this change?
To test the changes, I ran
yarn run test
. There were some errors, but they probably have nothing to do with the provided code: