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

Context API bitmask related questions #12732

Closed
simon998yang opened this Issue May 2, 2018 · 9 comments

Comments

Projects
None yet
4 participants
@simon998yang

simon998yang commented May 2, 2018

I'm playing with Context API bitmask feature to bail out unwanted re-render.

I have a dynamic model ( a JSON object) as context value. By dynamic i meant , the number of keys and structure of the JSON object is unknown. But when the JSON object changed , i know which key is changed. It seems difficult to turn the unknown keys to static pre-defined bitmasks. But I thought such use case is very common, and the bail-out feature should handle it easily by just matching the key.

Also I notice default changedBits and observedBits is MAX_SIGNED_31_BIT_INT. Does this mean it has a limitation up to 31 type of context change?

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon May 2, 2018

Member

The bailout feature is intended for advanced libraries like Relay so it's not very user-friendly (a compromise necessary to make it really fast). Indeed, you're limited to up to 31 type of context change. Nothing prevents you from using one bit for several fields when you have too many.

Member

gaearon commented May 2, 2018

The bailout feature is intended for advanced libraries like Relay so it's not very user-friendly (a compromise necessary to make it really fast). Indeed, you're limited to up to 31 type of context change. Nothing prevents you from using one bit for several fields when you have too many.

@simon998yang

This comment has been minimized.

Show comment
Hide comment
@simon998yang

simon998yang May 3, 2018

Thanks for the prompt response. Maybe same concept like selector function in redux mapStateToProps could be introduced here? i.e the Context.Consumer accept a selector function (Memoized as well) to select the concerned part from context?

simon998yang commented May 3, 2018

Thanks for the prompt response. Maybe same concept like selector function in redux mapStateToProps could be introduced here? i.e the Context.Consumer accept a selector function (Memoized as well) to select the concerned part from context?

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon May 3, 2018

Member

We're intentionally not using something like this for consumers because that means arbitrary code would run during traversal. Which inevitably will end up being too slow. The cool thing about the bitmask API is that no user code runs per consumer at all, which is what keeps traversal fast.

You can do regular memoization with other strategies like shouldComponentUpdate. And you can build some system based on selectors on top, e.g. aweary/react-copy-write#9, although you'd need to carefully benchmark this to ensure you're not making it slower.

Member

gaearon commented May 3, 2018

We're intentionally not using something like this for consumers because that means arbitrary code would run during traversal. Which inevitably will end up being too slow. The cool thing about the bitmask API is that no user code runs per consumer at all, which is what keeps traversal fast.

You can do regular memoization with other strategies like shouldComponentUpdate. And you can build some system based on selectors on top, e.g. aweary/react-copy-write#9, although you'd need to carefully benchmark this to ensure you're not making it slower.

@gaearon gaearon closed this May 3, 2018

@simon998yang

This comment has been minimized.

Show comment
Hide comment
@simon998yang

simon998yang May 3, 2018

wow, I did not expect a instant reply! :),

I got the point, no arbitrary code during traversal, though not fully convinced. shouldComponentUpdate is arbitrary code as well, and is on critical traversal path as well. Still think put this selector function at same level as unstable_observedBits is better. For example, if I use shouldComponentUpdate in my component
to bail-out re-render , with react-dev-tool highlight updates. I see all the consumer still flashing.., but if I use unstable_observedBits to bailout, then very clean, only the concern component is flashing...

I will check aweary/react-copy-write#9 once i got some more time.
cheers!

simon998yang commented May 3, 2018

wow, I did not expect a instant reply! :),

I got the point, no arbitrary code during traversal, though not fully convinced. shouldComponentUpdate is arbitrary code as well, and is on critical traversal path as well. Still think put this selector function at same level as unstable_observedBits is better. For example, if I use shouldComponentUpdate in my component
to bail-out re-render , with react-dev-tool highlight updates. I see all the consumer still flashing.., but if I use unstable_observedBits to bailout, then very clean, only the concern component is flashing...

I will check aweary/react-copy-write#9 once i got some more time.
cheers!

@simon998yang

This comment has been minimized.

Show comment
Hide comment
@simon998yang

simon998yang May 3, 2018

I had a look at aweary/react-copy-write#9, feeling that the bailout feature 'unstable_observedBits' offers is so essential to the context API, and the use case is kind of universal. should not let people 'try so hard go so far' to use it :).

hope you can give it a second thought! make the context API support selector function natively.

simon998yang commented May 3, 2018

I had a look at aweary/react-copy-write#9, feeling that the bailout feature 'unstable_observedBits' offers is so essential to the context API, and the use case is kind of universal. should not let people 'try so hard go so far' to use it :).

hope you can give it a second thought! make the context API support selector function natively.

@drcmda

This comment has been minimized.

Show comment
Hide comment
@drcmda

drcmda Jun 9, 2018

I got the point, no arbitrary code during traversal, though not fully convinced. shouldComponentUpdate is arbitrary code as well, and is on critical traversal path as well

@gaearon Using context as a global state container without a selector is not feasible, everything will render on any change, leading to grave performance issues. And using it just for small values and atomics is wasted potential.

Why would a selector make things slower? If it's not present React can go about its usual route, but if it is present by the users explicit intent then it should shallow-diff user-provided props, just like PureComponent, which is equally explicit, performs the shallow-diff for its own props.

<Consumer select={state => ({ user: state.users[this.props.userId] })}>
  {({ user }) => <User {...user} /> }
</Consumer>

In the above case, what good would come out of rendering that particular user-tied view on any change to the store and any change to any user that doesn't concern this view? And state-libs that wrap consumers in hocs that can select only cause further complication.

With this one small addition state in React would be a solved issue.

drcmda commented Jun 9, 2018

I got the point, no arbitrary code during traversal, though not fully convinced. shouldComponentUpdate is arbitrary code as well, and is on critical traversal path as well

@gaearon Using context as a global state container without a selector is not feasible, everything will render on any change, leading to grave performance issues. And using it just for small values and atomics is wasted potential.

Why would a selector make things slower? If it's not present React can go about its usual route, but if it is present by the users explicit intent then it should shallow-diff user-provided props, just like PureComponent, which is equally explicit, performs the shallow-diff for its own props.

<Consumer select={state => ({ user: state.users[this.props.userId] })}>
  {({ user }) => <User {...user} /> }
</Consumer>

In the above case, what good would come out of rendering that particular user-tied view on any change to the store and any change to any user that doesn't concern this view? And state-libs that wrap consumers in hocs that can select only cause further complication.

With this one small addition state in React would be a solved issue.

@TrySound

This comment has been minimized.

Show comment
Hide comment
@TrySound

TrySound Jun 9, 2018

Contributor

@drcmda What is the difference with this?

class User extends React.PureComponent {
  render() {}
}

<Consumer>
  {({ state }) => <User {...state.users[this.props.userId]} /> }
</Consumer>
Contributor

TrySound commented Jun 9, 2018

@drcmda What is the difference with this?

class User extends React.PureComponent {
  render() {}
}

<Consumer>
  {({ state }) => <User {...state.users[this.props.userId]} /> }
</Consumer>
@drcmda

This comment has been minimized.

Show comment
Hide comment
@drcmda

drcmda Jun 9, 2018

It would inhibit inline views. I mean, you could do the same in redux: connect all components to the state containers root, spread values and leave it to PureComponent. But would Dan and Mark agree that this is a good pattern? I guess most people would prefer mSTP as it makes it less verbose, more explicit (and perhaps faster?).

A selector shouldn't affect how consumers are working right now. If no selector is given, React traverses through like always, if there's one it diffs. React would have a scaleable state-model that's straight forward and easy to understand, even to beginners.

drcmda commented Jun 9, 2018

It would inhibit inline views. I mean, you could do the same in redux: connect all components to the state containers root, spread values and leave it to PureComponent. But would Dan and Mark agree that this is a good pattern? I guess most people would prefer mSTP as it makes it less verbose, more explicit (and perhaps faster?).

A selector shouldn't affect how consumers are working right now. If no selector is given, React traverses through like always, if there's one it diffs. React would have a scaleable state-model that's straight forward and easy to understand, even to beginners.

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Jun 9, 2018

Member

If you want to run selectors then the model above (consumer + sCU in a component) is the right one.

Bimask is a lower level API for extreme use cases like Relay.

Member

gaearon commented Jun 9, 2018

If you want to run selectors then the model above (consumer + sCU in a component) is the right one.

Bimask is a lower level API for extreme use cases like Relay.

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