React Context 不适用于服务器端渲染

Posted

技术标签:

【中文标题】React Context 不适用于服务器端渲染【英文标题】:React Context not working with server side rendering 【发布时间】:2019-08-05 02:11:05 【问题描述】:

我正在尝试让反应上下文与 s-s-r 一起使用。这就是我所拥有的

// server/index.s

import express from "express";
import serverRenderer from "./middleware/renderer";
const PORT = 3000;
const path = require("path");
const app = express();
const router = express.Router();
router.use("^/$", serverRenderer);

app.use(router);
app.listen(PORT, error => 
  console.log("listening on 3000 from the server");
  if (error) 
    console.log(error);
  
);

这是渲染器的样子-

export default (req, res, next) => 
  const filePath = path.resolve(__dirname, "..", "..", "..", "index.html");
  fs.readFile(filePath, "utf8", (err, htmlData) => 
    if (err) 
      console.log("err", err);
      return res.status(404).end();
    
    const store = configureStore();
    store.dispatch(getDesktopFooter(`$req.url`)).then(data => 
      const preloadedState = store.getState();
      const TestContext = React.createContext(
        hello: "hello"
      );
      const renderedBody = ReactDOMServer.renderToStaticMarkup(
        <TestContext.Provider value= hello: "hello" >
          <DummyApp />
        </TestContext.Provider>
      );

      // const renderedBody = "";
      //render the app as a string
      const helmet = Helmet.renderStatic();

      //inject the rendered app into our html and send it
      // Form the final HTML response
      const html = prepHTML(htmlData, 
        html: helmet.htmlAttributes.toString(),
        head:
          helmet.title.toString() +
          helmet.meta.toString() +
          helmet.link.toString(),
        body: renderedBody,
        preloadedState: preloadedState
      );

      // Up, up, and away...
      return res.send(html);
    );
  );
;

我的 DummyApp 看起来像

import React from "react";
import Test from "./Test";
import  default as AppStyles  from "./App.css";
export default class DummyApp extends React.Component 
  render() 
    console.log("DUMMY APP CONTEXT");
    console.log(this.context);
    return (
      <React.Fragment>
        <div className=AppStyles.base>
          <Test />
        </div>
      </React.Fragment>
    );
  

上下文总是,而它应该是hello: "hello"

为什么会这样?

【问题讨论】:

您的DummyApp 中似乎没有上下文Consumer 但是文档说我不需要?reactjs.org/docs/context.html#dynamic-context 你链接的那个例子是设置组件的contextType,你也没有这样做。 【参考方案1】:

您需要使用组件中的上下文才能读取它。

您还需要在服务器渲染函数之外创建TestContext,以便您的组件可以导入并使用它。

示例

// TestContext.js
export default TestContext = React.createContext(
  hello: "hello"
);

// server.js
const TestContext = require("./TestContext.js")

export default (req, res, next) => 
  // ...
  const renderedBody = ReactDOMServer.renderToStaticMarkup(
    <TestContext.Provider value= hello: "hello" >
      <DummyApp />
    </TestContext.Provider>
  );
  // ...
;

// DummyApp.js
import TestContext from "./TestContext.js";

export default class DummyApp extends React.Component 
  static contextType = TestContext;

  render() 
    console.log(this.context);
    return (
      <React.Fragment>
        <div className=AppStyles.base>
          <Test />
        </div>
      </React.Fragment>
    );
  

【讨论】:

【参考方案2】:

所以@Tholle 在技术上是正确的,但还有另一个问题是真正的问题。这方面的内容不多,所以如果有人遇到同样的问题,我希望他们能阅读这个答案。

contextType 仅适用于 react 版本 >= 16.6.0

React 文档中没有提到这一点 - https://reactjs.org/docs/context.html#classcontexttype,但奇怪的是这里提到了

https://scotch.io/bar-talk/whats-new-in-react-166

【讨论】:

以上是关于React Context 不适用于服务器端渲染的主要内容,如果未能解决你的问题,请参考以下文章

React Context API 不适用于单个应用程序对话框

React-Slick 不适用于 React 中的第一次渲染

React - 新的 Context API 不适用于 Class.contextType,但适用于 Context.Consumer

服务器端渲染不适用于生产构建 Next.js

Css 过渡不适用于 React 中的条件渲染

用于服务器端渲染的 create-react-app 打字稿