代码之家  ›  专栏  ›  技术社区  ›  Tyler Sebastian

SSR中的NextJS轨道安装组件

  •  8
  • Tyler Sebastian  · 技术社区  · 7 年前

    我使用React的上下文来跟踪某些已安装组件的id。要点是

    class Root extends React.Component {
      getChildContext () {
        return {
          registerComponent: this.registerComponent
        }
      }
    
      registerComponent = (id) => {
        this.setState(({ mountedComponents }) => {
          return { mountedComponents: [...mountedComponents, id ] }
        })
      }
    
      ...
    }
    
    class ChildComponent {
      static contextTypes = { registerComponent: PropTypes.func }
    
      constructor(props) {
        super(props)
    
        props.registerComponent(props.id)
      }
    }
    

    this.state.mountedComponents 总是 [] 在服务器上。有没有其他方法来跟踪这些部件 服务器端 ? 基本上,我需要id来提供脚本,以便在 head 在文档中,等待客户端应用程序手动装载、运行并附加到头部是有点太慢了。

    更新

    下面是一个快速的回购示例: https://github.com/tills13/nextjs-ssr-context

    this.context undefined Child ,如果我把它移到 componentDidMount context ,如果有别的办法的话,我洗耳恭听。

    2 回复  |  直到 7 年前
        1
  •  3
  •   Tarun Lalwani    7 年前

    我已经深入研究了这个问题,我的感觉是,这不可能有多种原因。

    这个 setState

    你的 provider

    我把不同的 console.log 下面是我得到的

    Provider constructed with { mountedComponents: [],
      name: 'name-Sun Jun 24 2018 19:19:08 GMT+0530 (IST)' }
    getDerivedStateFromProps
    Renderring Provider Component { mountedComponents: [],
      name: 'name-Sun Jun 24 2018 19:19:08 GMT+0530 (IST)' }
    data loaded
    constructor Child { id: '1' } { registerComponent: [Function: value] }
    Register Component called 1 { mountedComponents: [],
      name: 'name-Sun Jun 24 2018 19:19:08 GMT+0530 (IST)' }
    Registering component 1
    constructor Child { id: '2' } { registerComponent: [Function: value] }
    Register Component called 2 { mountedComponents: [ '1' ],
      name: 'tarun-Sun Jun 24 2018 19:19:08 GMT+0530 (IST)' }
    Registering component 2
    constructor Child { id: '3' } { registerComponent: [Function: value] }
    Register Component called 3 { mountedComponents: [ '1', '2' ],
      name: 'name-Sun Jun 24 2018 19:19:08 GMT+0530 (IST)' }
    Registering component 3
    constructor Child { id: '4' } { registerComponent: [Function: value] }
    Register Component called 4 { mountedComponents: [ '1', '2', '3' ],
      name: 'name-Sun Jun 24 2018 19:19:08 GMT+0530 (IST)' }
    Registering component 4
    

    这些信息实际上只有在 componentDidMount 发生。但由于在服务器端渲染时不会发生这种情况,因此无法获取数据。

    我正在进一步调查是否有黑客可用,但目前还不确定

        2
  •  2
  •   Lafi    7 年前

    构造函数中的API是可能的,您只需要将其作为参数传递到 构造器 超级的

    export class Child extends React.Component {
      static contextTypes = { registerComponent: PropTypes.func };
    
      constructor(props, context) {
        super(props, context);
        context.registerComponent(props.id);
      }
    
      render() {
        return <div id={this.props.id} />;
      }
    }
    

    更新:
    所以,问题与 服务器 不支持任何活动间, 这种做法将阻止 setState 考虑 ,在您的示例中 <Provider /> 不会伸出援手,所以我们需要一个解决方案,无论如何都是一个黑客。

    我得到的解决办法是 window.ids 对于每个跟踪的组件呈现(使用nextjs提供的头):

    export class Child extends React.Component {
      registerComponent = (id) => `
        if (window.ids) {
          window.ids = [...window.ids, ${id}];
        } else window.ids = [${id}];
      `;
    
      render() {
        return (
          <Fragment>
            <Head>
              <script
                className="track"
                dangerouslySetInnerHTML={{
                  __html: `${this.registerComponent(this.props.id)}`,
                }}
              />
            </Head>
            <div id={this.props.id} />
          </Fragment>
        );
      }
    }
    

    所以 变量将在 <App /> renders
    这是一个 repos link

    另一个解决方案可能是使用 全局变量 在服务器上,然后在每个被跟踪的组件中,我们可以进行变异 使用 生命周期挂钩,因为它只在服务器上执行,然后 在html模板上注入这些id <head /> ,但是如果我们执行renderToString方法,这是可能的。

    第二种解决方案 _文件 而不是页面/ _应用程序 before it renders to string! :
    这是回购分行: origin/workaround-using-document

    -子组件:

    export class Child extends React.Component {
      render() {
        return (
          <Fragment>
            <Head>
              <meta className="track" id={this.props.id} />
            </Head>
            <div id={this.props.id} />
          </Fragment>
        );
      }
    }
    

    export default class MyDocument extends Document {
      render() {
        const { head } = this.props;
        const idsFromHead = head.reduce(
          (acc, { props }) =>
            (props.className.includes('track') && [...acc, props.id]) || acc,
          []
        );
    
        return (
          <html>
            <Head>
              <script
                dangerouslySetInnerHTML={{
                  __html: `window.ids=[${idsFromHead}]`,
                }}
              />
            </Head>
            <body className="custom_class">
              <h3>{idsFromHead}</h3>
              <Main />
              <NextScript />
            </body>
          </html>
        );
      }
    }
    

    此方法在服务器上100%工作,因为我们可以在服务器呈现之前捕获跟踪的ID。 enter image description here

    但是 <NextScript /> 正在生成警告(不知道为什么,可能是nextjs的错误):
    enter image description here