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.

RFC: selective context propagation

See original GitHub issue

Selective context propagation

I see the comparison of Redux to Context quite often lately and wanted to act on this.

Differences

Redux is performant by nature, it uses the concept of selectors to memoize values and rerender selectively, context on the other hand will propagate through to every Consumer subscribed to the whole context.

The clear difference between these two we can notice is that when x amount of components are subscribed to individual pieces of state, for redux this will rerender the amount of times the state has changed while with the current form of context this will be x.

Prerequisites

One of the big prerequisites we’re lacking out for this concept to succeed is strict-equality on vnodes, this means that if the children did not change from render 1 --> render 2 we won’t have to evaluate these children. This is a very common pattern when it comes to contextProviders.

Let’s consider the following scenario:

const App = () => (
  <MyProvider>
    <MyRoutes />
  </MyProvider>
);

When <App /> updates this whole tree will rerender since the function will execute and reevaluate all these children, let’s for clarity look at a hypothetical <MyProvider /> implementation.

const { Provider } = createContext();
const MyProvider = ({ children }) => {
  const [state, setState] = useState();
  return (
    <Provider value={{ state, setState }}>
      {children}
    </Provider>
  )
}

As we can see there’s a prop injected named children, if this doesn’t change the vnode is equal and shouldn’t be diffed. This means that if a Consumer calls setState the MyProvider component will alter its internal state but the children, injected by <App /> (which hasn’t reexecuted), will still be the same so we can safely decide to not diff this part.

This means that instead of walking the tree downwards we’ll only trigger the contextConsumers.

Current PR

Implementation

To come back to our selective context propagation, we have two options to support this effectively.

Option 1

We can supply a rerender function similar to shouldComponentUpdate, useContext/others could allow a second argument (or a new hook) that allows the user to selectively compare the old and new context and decide wether this hook needs an emit. This would always return the full context (important note when reading the next proposition).

const MyName = () => {
  const { name } = useContext(userContext, (prev, curr) => prev.name !== curr.name);
  return <p>Welcome {name}</p>;
}

Option 2

Selector-style, for this case useContext/others can accept an argument that selects a certain value out of context, if this value hasn’t changed compared to the previous instance it won’t cause a rerender. The return value of useContext/others will be the selected value as opposed to the full context.

const MyName = () => {
  const name = useContext(userContext, ctx => ctx.name);
  return <p>Welcome {name}</p>;
}

Concluding

Option 1 will probably be the smallest, size-wise and introduce less context retrievals in general. This means that we have less calls to useContext/less nesting of context.Consumer since if we’d need more properties we’d need to introduce more selectors (or complex ones). Less useContext calls means less comparisons which can only benefit performance + for option 1 we have the current infrastructure ready to go.

This RFC aims at bringing out of the box perf improvements to Preact.createContext not a replacement of Redux. There are many things that are in Redux that would have to be reimplemented in user land (in the form of libraries potentially) to replace for instance, logging, …

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
porfirioribeirocommented, Mar 20, 2020

@robertknight i think that also happens with Redux, At least when i worked with Redux last time, i remember having some issues with the return of mapStateToProps. What redux does is that it always assume you return a object and do a shallow equals to avoid that rerender, but still you can mess with object equality at a different depth and get too much rendering.

1reaction
JoviDeCroockcommented, Apr 24, 2020

Thanks a lot for all of this information @gnoff I’ll be certain to look into all of this in the near future, we’ll clear this up asap.

Also thanks a lot for pointing out a bug, we never thought about this case https://github.com/preactjs/preact/pull/2501

Read more comments on GitHub >

github_iconTop Results From Across the Web

Context Transfer Protocol (CXTP) RFC 4067 - IETF Datatracker
Experimental [Page 1] RFC 4067 Context Transfer Protocol (CXTP) July 2005 5.3. ... wireless link technology and local wireless propagation conditions.
Read more >
Trace Context - W3C
This specification defines standard HTTP headers and a value format to propagate context information that enables distributed tracing ...
Read more >
= "Perl 6 RFC Index" - Raku Programming Language
RFC details by Working Group can be found here. ... 45 (v3): C<||> and C<&&> should propagate result context to both sides [HTML];...
Read more >
RFC 2018: TCP Selective Acknowledgment Options | ICSI
A Selective Acknowledgment (SACK) mechanism, combined with a selective repeat retransmission policy, can help to overcome these limitations.
Read more >
RFC 4163 - RObust Header Compression (ROHC) - Tech-invite
Restyled version of RFC 4163: RObust Header Compression (ROHC): ... In such cases, loss propagation due to header compression could affect certain TCP ......
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