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.

Imperative wrappers can't access current context value in commit phase

See original GitHub issue

Sometimes we have an imperative wrapper like this:

componentDidMount() {
  renderSomethingImperatively(this.props)
}

componentDidUpdate() {
  renderSomethingImperatively(this.props)
}

render() {
  return null
}

Portals eliminated the need for this for regular DOM jumps. But we still need this for embedding renderers (e.g. react-art does this) and use cases like “Vue inside React”.

For cross-renderer embedding, maybe we could extend portals to do that (https://github.com/facebook/react/issues/13332). There are still imperative use cases for cross-library rendering though.

One thing that becomes annoying is that new context won’t propagate down through this imperative boundary. This is because we don’t maintain a stack in the commit phase. We’re traversing a flat linked list of effects. So we don’t actually know what context value is current by the time componentDidMount or componentDidUpdate fires.

For react-art and friends, this means context from a host app is not accessible. This is quite annoying. You could hack around it with something like

<MyConsumer>
  {value =>
    <ReactART.Surface>
      <MyContext.Provider value={value}>
        <Stuff />
      </MyContext.Provider>
    </ReactART.Surface>
  }
</MyConsumer>

But this is neither obvious nor convenient. You have to anticipate all contexts that can get used below.

This seems even less convenient for imperative cases like “Vue inside React”.

componentDidMount() {
  renderSomethingImperatively(this.props) // ???
}

componentDidUpdate() {
  renderSomethingImperatively(this.props) // ???
}

render() {
  // <MyConsumer>{value => ???}</MyConsumer>
  return <div />
}

Seems like you could use unstable_read() in getDerivedStateFromProps and that would put it into state so you can use it in lifecycles. So maybe that’s sufficient. It still means you need to be explicit about which contexts you want to remember though.

I wonder if we can find a better solution to these use cases.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:5
  • Comments:7 (2 by maintainers)

github_iconTop GitHub Comments

8reactions
lyleunderwoodcommented, Aug 21, 2018

Here’s my hacky workaround to this which seems to function but could stand to be generalized:

// @flow
import * as React from 'react';

import { L10nProvider, L10nConsumer } from './l10n-context';
import { SettingsProvider, SettingsConsumer } from './settings-context';

// TODO: react does not currently support seamlessly passing context
// between portals, and therefore renderers, including react-konva
// (canvas renderer). see:
// https://github.com/konvajs/react-konva/issues/188

type Props = {
  children: React.Element<any>,
  barrierRender: (React.Element<any>) => React.Element<any>,
};

const ContextBridge = ({ barrierRender, children }: Props) => (
  <L10nConsumer>{l10nValue => (
    <SettingsConsumer>{settingsValue => (
      barrierRender(
        <L10nProvider value={l10nValue}>
          <SettingsProvider value={settingsValue}>
            {children}
          </SettingsProvider>
        </L10nProvider>
      )
    )}</SettingsConsumer>
  )}</L10nConsumer>
);

export default ContextBridge;

and a usage example (react-konva):

      <ContextBridge
        barrierRender={children => (
          <Stage width={500} height={300} scaleX={1} scaleY={1}>
            {children}
          </Stage>
        )}
      >
        <Layer>
          <L10nConsumer>{({ locale }) => (
            <Text
              text={locale}
            />
          )}</L10nConsumer>
        </Layer>
      </ContextBridge>

1reaction
sebmarkbagecommented, Aug 7, 2018

My personal preference is MyClass.contextType = MyContext; and the value gets automatically set on this.context just like the old one but for single values. If you use more than one here, we need to have a hard conversation about your context abuse.

Read more comments on GitHub >

github_iconTop Results From Across the Web

React context not updating - Stack Overflow
Think about React context just like you would a component, if you want to update a value and show it then you need...
Read more >
Apache Kafka Reference Guide - Quarkus
This reference guide demonstrates how your Quarkus application can utilize SmallRye Reactive Messaging to interact with Apache Kafka.
Read more >
Manipulating the DOM with Refs - React Docs
How to access a DOM node managed by React with the ref attribute; How the ref JSX ... For an introduction to refs,...
Read more >
What's New in SQLAlchemy 1.4?
join() and outerjoin() add JOIN criteria to the current query, rather than creating a subquery for background on this. Statements that work with...
Read more >
Transforming the future: anticipation in the 21st century
Hunting for2 Riel Miller different kinds of futures runs directly into the obstacle that by definition the future cannot exist in the present,...
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