如何防止使用React(next.js)SSR两遍渲染来重新渲染父组件?

时间:2019-11-22 03:42:54

标签: reactjs next.js server-side-rendering

所以我有一个使用Next.js的SSR应用程序。我正在使用利用WEB API的第三方组件,因此需要将其加载到客户端而不是服务器上。我是通过“两次通过”渲染来实现的,在这里我了解到:https://itnext.io/tips-for-server-side-rendering-with-react-e42b1b7acd57

我试图弄清楚为什么当next.js页面中的ssDone状态更改时,整个<Layout>组件不必要地重新渲染,包括页面的Header,Footer等。

我已经了解了React.memo()并利用了shouldComponentUpdate(),但似乎无法阻止它重新呈现<Layout>组件。

我的<Layout>控制台消息触发两次,但<ThirdPartyComponent>控制台消息触发一次。这是一个问题还是React足够聪明以至于实际上没有更新DOM,所以我什至不必担心这一点。无缘无故地重新渲染页面的页眉和页脚似乎很愚蠢。

在控制台中,输出为:

Layout rendered
Layout rendered
3rd party component rendered

index.js(next.js页面)

import React from "react";
import Layout from "../components/Layout";
import ThirdPartyComponent from "../components/ThirdPartyComponent";

class Home extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      ssrDone: false
    };
  }

  componentDidMount() {
    this.setState({ ssrDone: true });
  }

  render() {
    return (
      <Layout>
        {this.state.ssrDone ? <ThirdPartyComponent /> : <div> ...loading</div>}
      </Layout>
    );
  }
}

export default Home;

ThirdPartyComponent.jsx

import React from "react";

export default function ThirdPartyComponent() {
  console.log("3rd party component rendered");
  return <div>3rd Party Component</div>;
}

Layout.jsx

import React from "react";

export default function Layout({ children }) {
  return (
    <div>
      {console.log("Layout rendered")}
      NavBar here
      <div>Header</div>
      {children}
      <div>Footer</div>
    </div>
  );
}

2 个答案:

答案 0 :(得分:0)

Layout组件重新呈现,因为其children道具已更改。首先是<div> ...loading</div>(当ssrDone = false时),然后是<ThirdPartyComponent />(当ssrDone = true时)

答案 1 :(得分:0)

您可以做的是定义一个新的<ClientSideOnlyRenderer />组件,它看起来像这样:

const ClientSideOnlyRenderer = memo(function ClientSideOnlyRenderer({
  initialSsrDone = false,
  renderDone,
  renderLoading,
}) {
  const [ssrDone, setSsrDone] = useState(initialSsrDone);

  useEffect(
    function afterMount() {
      setSsrDone(true);
    },
    [],
  );

  if (!ssrDone) {
    return renderLoading();
  }

  return renderDone();
});

您可以这样使用它:

class Home extends React.Component {
  static async getInitialProps({ req }) {
    return {
      isServer: !!req,
    };
  };

  renderDone() {
    return (
      <ThirdPartyComponent />
    );
  }
  renderLoading() {
    return (<div>Loading...</div>);
  }

  render() {
    const { isServer } = this.props;

    return (
      <Layout>
        <ClientSideOnlyRenderer
          initialSsrDone={!isServer}
          renderDone={this.renderDone}
          renderLoading={this.renderLoading}
        />
      </Layout>
    );
  }
}

这样,在初始安装后,只有ClientSideOnlyRenderer组件会重新呈现。 ?