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

React 18: Context providers are reset to initial value in SSR during rendering #23089

Closed
frandiox opened this issue Jan 10, 2022 · 5 comments · Fixed by #23171
Closed

React 18: Context providers are reset to initial value in SSR during rendering #23089

frandiox opened this issue Jan 10, 2022 · 5 comments · Fixed by #23171
Labels
React 18 Bug reports, questions, and general feedback about React 18 Type: Bug

Comments

@frandiox
Copy link
Contributor

While testing SSR streaming in latest React 18 experimental and alpha versions, we noticed that context providers are reset to their initial values during rendering under certain conditions.
It works well when handling 1 request at a time. However, when the server gets 2 or more requests at the same time, the context providers seem to get confused. The context is correct at the beginning of the rendering for each request but it gets lost after a while.

There's a reproduction here using @gaearon 's demo: https://codesandbox.io/s/keen-snowflake-8nyo8?file=/src/data.js:1035-1082

To my understanding, since the React tree is wrapped in a provider in SSR, useContext should never return null in the server. Have a look at the terminal and see how it actually logs null sometimes when getting multiple requests.

[0] This should never be null: { read: [Function: read] }
[0] This should never be null: { read: [Function: read] }
[0] This should never be null: null
[0] This should never be null: null

Run the following code from the console to simulate multiple requests:

function doRequest() { return fetch('https://8nyo8.sse.codesandbox.io/', {headers: {accept:'text/html'}}).then(r => r.text()) }
await Promise.all([doRequest(), doRequest()])

We saw this same issue in different setups, using both Webpack and Vite.

Thanks!

@frandiox frandiox added React 18 Bug reports, questions, and general feedback about React 18 Type: Discussion labels Jan 10, 2022
@zythum
Copy link

zythum commented Jan 13, 2022

yes, i got the same problem. when got more then 2 requests at the same time, the context providers will not work.

this is my code:

react / react-dom version is 18.0.0-rc.0-next-9a7e6bf0d-20220111

import http from "http";
import React from "react";
import ReactDOMServer from "react-dom/server";

class DelayClient {
  public resolved?: string;
  public pending?: Promise<void>
  get() {
    if (this.resolved) return this.resolved;
    if (this.pending) return this.pending;
    return this.pending = new Promise(resolve => {
      setTimeout(() => {
        delete this.pending;
        this.resolved = "OK";
        resolve();
        console.log("timeout");
      }, 2000);
    });
  }

}
const DelayContext = React.createContext<DelayClient | undefined>(undefined);

// this is the component that use delay
// when a single request coming, this is OK. delay 2 seconds then got a "OK" from context
// but if mutli request (refresh website fastly), it will delay 2 seconds then context is undefined.
const Component: React.FC = () => {
  const client = React.useContext(DelayContext);
  if (!client) {
    return <div>context not found.</div>;
  }
  const result = client.get();
  if (typeof result === "string") {
    return <div>{result}</div>
  }
  throw result;
}

const app = http.createServer((req, res) => {

  if (req.url !== "/") {
    res.statusCode = 200;
    res.end();
    return;
  }

  // every request has a DelayClient to wait 2 seconds
  // then get a "OK" from DelayClient through context
  const client = new DelayClient();
  const element = <html>
    <head>
      <title>sample</title>
    </head>
    <body>
      <DelayContext.Provider value={client}>
        <React.Suspense fallback="loading">
          <Component/>
        </React.Suspense>
      </DelayContext.Provider>
    </body>
  </html>;
  const { pipe } = (ReactDOMServer as any).renderToPipeableStream(element, {
    onError(error: any) {
      console.log("error", error);
    },
    onCompleteShell() {
      res.writeHead(200, { "Content-Type": "text/html; charset=UTF-8" });
      pipe(res);
    },
  });
});
app.listen(3000, () => {
  console.log("app lift: http://127.0.0.1:3000/")
});

@gaearon
Copy link
Collaborator

gaearon commented Jan 13, 2022

Can you turn this into a failing test? Search the tests for existing tests using renderToPipeableStream.

@zythum
Copy link

zythum commented Jan 18, 2022

Failing test here. #23129 @gaearon

@sebmarkbage
Copy link
Collaborator

It's supposed to get reset here when restarting work after switching between working on different tasks (whether they're part of the same request or multiple).

switchContext(task.context);

gaearon added a commit that referenced this issue Jan 24, 2022
* add failing test for renderToPipeableStream

* Fix context providers in SSR when handling multiple requests. Closes #23089

* Add sibling regression test

Co-authored-by: zhuyi01 <[email protected]>
Co-authored-by: Dan Abramov <[email protected]>
@gaearon
Copy link
Collaborator

gaearon commented Jan 24, 2022

The fix is out in 18.0.0-rc.0-next-529dc3ce8-20220124.

zhengjitf pushed a commit to zhengjitf/react that referenced this issue Apr 15, 2022
…k#23171)

* add failing test for renderToPipeableStream

* Fix context providers in SSR when handling multiple requests. Closes facebook#23089

* Add sibling regression test

Co-authored-by: zhuyi01 <[email protected]>
Co-authored-by: Dan Abramov <[email protected]>
nevilm-lt pushed a commit to nevilm-lt/react that referenced this issue Apr 22, 2022
…k#23171)

* add failing test for renderToPipeableStream

* Fix context providers in SSR when handling multiple requests. Closes facebook#23089

* Add sibling regression test

Co-authored-by: zhuyi01 <[email protected]>
Co-authored-by: Dan Abramov <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
React 18 Bug reports, questions, and general feedback about React 18 Type: Bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants