question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Why calculateChangedBits = () => 0

See original GitHub issue

Background

React-Redux v6 (RR6) introduced what I’d call the state-based context value. The idea was to let React propagate re-render instead of triggering re-render from outside of React. This makes it easy to ensure components render top-down the component tree. Unfortunately, a way to bail out with useContext didn’t come, as I understand because there’s no way to implement that in an efficient way. So, React-Redux v7 (RR7) went back to store context and subscriptions.

Problem (Hypothetical)

We are not sure what the final concurrent mode will look like, but @markerikson had a comment in the code.

// TODO We're reading the store directly in render() here. Bad idea?
// This will likely cause Bad Things (TM) to happen in Concurrent Mode.
// Note that we do this because on renders _not_ caused by store updates, we need the latest store state
// to determine what the child props should be.

My understanding is if a component reads a state from the store, it might not be consistent for all components in the tree. If React pauses rendering in the middle of the tree, Redux may update the store state. So, a component A and a component B could get different state even in a single render in the tree. I was hoping batchedUpdates solves this, but unless batchedUpdates run all renders synchronously, Redux has a chance to update the store state. If we could only read a state from the context like RR6, this problem should be solved.

That doesn’t mean all issues around concurrent mode are solved. a warning comment by @gaearon .

Solution

We specify calculateChangedBits = () => 0; so that React doesn’t propagate updates. Only a single Provider subscribes to the Redux store. All components under the Provider subscribe to the Provider. When the store state is updated, Provider re-renders which triggers listeners, subscribed components check updates (in useSelector) and forces render. When a component re-renders, it will read the state from the context, and we expect it’s consistent. (No evidence, but that’s how RR6 would have worked.)

Note, this still doesn’t solve the stale props issue in useSelector.

Regret

If we had this solution half a year ago, we would have been able technically to base RR6 to add hooks support. (Updating peer deps might still require major version up, though.) It’s too late and this doesn’t provide any constructive suggestion now, but it may give a hint hopefully.

Final note

I read once again the 14110 issue, and found @sebmarkbage 's comment.

E.g. setting changed bits to zero and then manually force updating the relevant children.

It’s already noted. I didn’t have enough understanding back then. But, this is it.

Issue Analytics

  • State:open
  • Created 4 years ago
  • Comments:16 (8 by maintainers)

github_iconTop GitHub Comments

3reactions
markeriksoncommented, Jun 28, 2019

Huh. @dai-shi , check out this RFC for React context changes by @gnoff :

https://github.com/gnoff/rfcs/pull/2

2reactions
markeriksoncommented, Jun 29, 2019

Yeah, it would have been something to consider. Not sure if it would have been the ultimate solution, but would definitely have been worth comparing.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to fix 'Expected the return value to be a 31-bit integer ...
How to fix 'Expected the return value to be a 31-bit integer' error react hooks ; my context. export const CountCtx = createContext(0,...
Read more >
A Secret parts of React New Context API | by Toru Kobayashi
observedBits are bits represents what values Consumer are observed. You can specify the observedBits as the Consumer's Props. const Foo = () =>...
Read more >
@fluentui/react-context-selector - npm
When a context value is changed, all components that are subscribed with useContext() will re-render. useContextSelector is recently proposed.
Read more >
React Context: a Hidden Power - DEV Community ‍ ‍
There won't be any observed bits if we put 0, and every bit is observed ... calculateChangedBits is passed as the second argument...
Read more >
Bitmasks and the new React Context API
const calculateChangedBits = (currentValue, nextValue) => nextValue.value % 2 === 0 ? 0b10 : 0b1; If you have more than two, or a...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found