Skip to content
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

nested contexts don't unwind correctly when server side rendering #12984

Closed
ericsoderberghp opened this issue Jun 5, 2018 · 7 comments
Closed

Comments

@ericsoderberghp
Copy link
Contributor

ericsoderberghp commented Jun 5, 2018

Do you want to request a feature or report a bug?

bug

What is the current behavior?

When different contexts are nested, as in they have different types, they do not unwind correctly when rendering via renderToString().

Here is a snippet that demonstrates the issue:

import React from 'react';
import { renderToString } from 'react-dom/server';

const valueA = { letter: 'A' };
const valueB = { letter: 'B' };
const LetterContext = React.createContext(valueA);
const NumberContext = React.createContext(undefined);

const html = renderToString((
  <LetterContext.Provider value={valueA}>
    <NumberContext.Provider>
      <LetterContext.Consumer>
        {letterValue => (
          <div>
            {letterValue.letter}
            <LetterContext.Provider value={valueB}>
              <LetterContext.Consumer>
                {innerValue => <div>{innerValue.letter}</div>}
              </LetterContext.Consumer>
            </LetterContext.Provider>
          </div>
        )}
      </LetterContext.Consumer>
      <LetterContext.Consumer>
        {value => <div>{value.letter}</div>}
      </LetterContext.Consumer>
    </NumberContext.Provider>
  </LetterContext.Provider>
));

console.log(html);

This results in:

          value.letter
                ^

TypeError: Cannot read property 'letter' of undefined
    at Object.children (/Users/ericsoderberg/Documents/git/grommet2/grommet-icons-site/tools/example.js:25:31)
    at ReactDOMServerRenderer.render (/Users/ericsoderberg/Documents/git/grommet2/grommet-icons-site/node_modules/react-dom/cjs/react-dom-server.node.development.js:2462:55)
    at ReactDOMServerRenderer.read (/Users/ericsoderberg/Documents/git/grommet2/grommet-icons-site/node_modules/react-dom/cjs/react-dom-server.node.development.js:2325:19)
    at renderToString (/Users/ericsoderberg/Documents/git/grommet2/grommet-icons-site/node_modules/react-dom/cjs/react-dom-server.node.development.js:2697:25)
    at Object.<anonymous> (/Users/ericsoderberg/Documents/git/grommet2/grommet-icons-site/tools/example.js:9:14)
    at Module._compile (internal/modules/cjs/loader.js:678:30)
    at loader (/Users/ericsoderberg/Documents/git/grommet2/grommet-icons-site/node_modules/babel-register/lib/node.js:144:5)
    at Object.require.extensions.(anonymous function) [as .js] (/Users/ericsoderberg/Documents/git/grommet2/grommet-icons-site/node_modules/babel-register/lib/node.js:154:7)
    at Module.load (internal/modules/cjs/loader.js:589:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:528:12)

What is the expected behavior?

When context providers are nested, they should correctly unwind such that the contexts return the correct value at each level. No exception should be generated and the output should be:

<div>A<div>B</div></div><div>A</div>

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?

This was uncovered using version 16.4.0 of react-dom on OSX using node v10.1.0.

This only shows up when using renderToString(). It works as expected in the browser.

** Note **

I wonder if this might be due to the following snippet of react-dom-server.node.development.js. Where the previous provider added to the stack is assumed to have the same type as the provider being popped.

    var context = provider.type._context;
    if (this.providerIndex < 0) {
      context._currentValue = context._defaultValue;
    } else {
      // We assume this type is correct because of the index check above.
      var previousProvider = this.providerStack[this.providerIndex];
      context._currentValue = previousProvider.props.value;
    }

I am investigating further and hope to provide a pull request soon.

@gaearon
Copy link
Collaborator

gaearon commented Jun 5, 2018

Is this the same as #12968?

@ericsoderberghp
Copy link
Contributor Author

I suspect so. I didn't see this when I searched for an existing issue, apologies.

@gaearon
Copy link
Collaborator

gaearon commented Jun 5, 2018

No problem. Still happy to take a fix :-)

@ericsoderberghp
Copy link
Contributor Author

Working on it ...

@ericsoderberghp
Copy link
Contributor Author

After further analysis, I don't think
#12968 is exactly the same issue. That issue revolves around multiple renders, whereas this one is just a single render. I suspect they are in the same area, but they aren't the same.

@aweary
Copy link
Contributor

aweary commented Jun 5, 2018

Both issues are symptoms of the same root bug related to how ReactPartialRender is handling context providers, so we can just track this in #12968.

@aweary aweary closed this as completed Jun 5, 2018
gaearon pushed a commit that referenced this issue Jun 11, 2018
…Issue #12984 (#12985)

* Fixed an issue with nested contexts unwinding when server rendering. GitHub issue #12984

* Fixed an issue with search direction and stricter false checking

* Use decrement infix operator

* Streamlined existence checks

* Streamlined assignment. Removed redundant comment. Use null for array values

* Made prettier

* Relaxed type checking and improved comment

* Improve test coverage
@gaearon
Copy link
Collaborator

gaearon commented Jun 13, 2018

Fixed in React 16.4.1.

grahammendick added a commit to grahammendick/navigation that referenced this issue Jun 15, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants