xgqfrms™, xgqfrms® : xgqfrms's offical website of cnblogs! xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!

Next.js & React component render twice bug All In One

Next.js & React component render twice bug All In One

error ❌

React.StrictMode

https://github.com/facebook/react/issues/15074

https://reactjs.org/docs/strict-mode.html

New Strict Mode Behaviors

https://reactjs.org/blog/2022/03/29/react-v18.html#new-strict-mode-behaviors

solution ✅

// next.config.js
module.exports = {
-  reactStrictMode: true,
+  reactStrictMode: false,
}

https://nextjs.org/docs/api-reference/next.config.js/react-strict-mode

demos

React 问题复现条件:

  1. state function component 有状态函数组件
  2. state class component 有状态函数组件
  3. hooks component 钩子组件

pure react app tested ❌

  1. stateless function component 无状态函数组件
  2. stateless class component 无状态函数组件

Next.js 问题复现条件:

  1. state function component 有状态函数组件

  2. state class component 有状态函数组件

  3. hooks component 钩子组件

  4. stateless function component 无状态函数组件

  5. stateless class component 无状态函数组件

import React, {
  useEffect,
  useRef,
} from "react";
import { createRoot } from 'react-dom/client';

// export default function Home() {
function Home() {
  const log = console.log;
  log(`v18 createRoot =\n`, createRoot);
  let root = useRef(null);
  useEffect(() => {
    // v18
    console.log(`root =`, root)
    console.log(`root.current =`, root.current)
    if(!root.current) {
      const App = document.getElementById('v18-app');
      root.current = createRoot(App);
      // JSX
      root.current.render(<h1>Develop. Preview. Ship. 🚀 React v18 🆕</h1>);
    }
  }, []);
  return (
    <div id="v18-app">...loading</div>
  )
};

export default React.memo(Home);


/*

Warning: You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before.
Instead, call root.render() on the existing root instead if you want to update it.


      // Assignments to the 'root' variable from inside React Hook useEffect will be lost after each render.
      // To preserve the value over time, store it in a useRef Hook and keep the mutable value in the '.current' property.
      // Otherwise, you can move this variable directly inside useEffect.eslintreact-hooks/exhaustive-deps
      // https://github.com/facebook/react/issues/14920


*/

React Component 性能优化

  1. PureComponent
import React,{PureComponent}  from "react";


class Welcome extends React.PureComponent {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

https://reactjs.org/docs/render-props.html#caveats


class Mouse extends React.PureComponent {
  // Same implementation as above...
}

class MouseTracker extends React.Component {
  // Defined as an instance method, `this.renderTheCat` always refers to *same* function when we use it in render
  renderTheCat(mouse) {
    return <Cat mouse={mouse} />;
  }

  render() {
    return (
      <div>
        <h1>Move the mouse around!</h1>
        <Mouse render={this.renderTheCat} />
      </div>
    );
  }
}

https://reactjs.org/docs/react-api.html#reactpurecomponent

  1. Function Component
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

  1. Class Component
import React,{Component}  from "react";

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

https://reactjs.org/docs/components-and-props.html

image

Since shouldComponentUpdate returned false for the subtree rooted at C2, React did not attempt to render C2, and thus didn’t even have to invoke shouldComponentUpdate on C4 and C5.

For C1 and C3, shouldComponentUpdate returned true, so React had to go down to the leaves and check them.
For C6 shouldComponentUpdate returned true, and since the rendered elements weren’t equivalent React had to update the DOM.

The last interesting case is C8.
React had to render this component, but since the React elements it returned were equal to the previously rendered ones, it didn’t have to update the DOM.

Note that React only had to do DOM mutations for C6, which was inevitable.
For C8, it bailed out by comparing the rendered React elements,
and for C2’s subtree and C7, it didn’t even have to compare the elements as we bailed out on shouldComponentUpdate, and render was not called.

import React,{Component}  from "react";

class Welcome extends React.Component {
  shouldComponentUpdate() {
    // 1.  如果 shouldComponentUpdate 返回值是  false,(子组件直接跳过了 👍)  就不需要进行 react element virtual dom 对比了,直接阻止了组件的重新渲染过程 🚀
    // 2. 如果 shouldComponentUpdate 返回值是  true, (子组件不能直接跳过,需要一层一层的遍历,重复该过程 👎)
    // 2.1 先进行 react element virtual dom 对比,如果组件没有变化,不需要重新渲染组件 ✅
    // 2.2 先进行 react element virtual dom 对比,如果组件变化了,需要重新渲染组件 ❌
    return false;
  }
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

https://reactjs.org/docs/optimizing-performance.html#shouldcomponentupdate-in-action

blogs

Extending from React.PureComponent or from React.Component with a custom shouldComponentUpdate method have performance implications.

class CustomPureComponent extends Component {
    shouldComponentUpdate (nextProps, nextState) {
        return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
    }
    // ...
}

https://stackoverflow.com/questions/40703675/react-functional-stateless-component-purecomponent-component-what-are-the-dif

HOC

higher-order components

https://reactjs.org/docs/higher-order-components.html

https://github.com/acdlite/recompose

https://github.com/acdlite/recompose/blob/master/docs/API.md#pure

import React from 'react';
import { pure } from 'recompose';

function PercentageStat({ label, score = 0, total = Math.max(1, score) }) {
  return (
    <div>
      <h6>{ label }</h6>
      <span>{ Math.round(score / total * 100) }%</span>
    </div>
  )
}

// Wrap component using the `pure` HOC from recompose
export default pure(PercentageStat);

React.memo

import React, { memo } from 'react';

function PercentageStat({ label, score = 0, total = Math.max(1, score) }) {
  return (
    <div>
      <h6>{ label }</h6>
      <span>{ Math.round(score / total * 100) }%</span>
    </div>
  )
}

// Wrap component using `React.memo()`
export default memo(PercentageStat);

arePropsEqual

import React, { memo } from 'react';

function PercentageStat({ label, score = 0, total = Math.max(1, score) }) {
  return (
    <div>
      <h6>{ label }</h6>
      <span>{ Math.round(score / total * 100) }%</span>
    </div>
  )
}

// 自定义 compare 方法 ✅
function arePropsEqual(prevProps, nextProps) {
  return prevProps.label === nextProps.label; 
}

// Wrap component using `React.memo()` and pass `arePropsEqual`
export default memo(PercentageStat, arePropsEqual);

https://blog.logrocket.com/what-are-react-pure-functional-components/#are-react-functional-components-pure

refs

https://github.com/web-full-stack/nextjs-ssr/issues/6#issuecomment-1315425580

next.js react components render twice bug ❌

https://deniapps.com/blog/why-the-react-component-renders-twice

https://www.heissenberger.at/en/blog/react-components-reder-twice/

https://mariosfakiolas.com/blog/my-react-components-render-twice-and-drive-me-crazy/



©xgqfrms 2012-2021

www.cnblogs.com/xgqfrms 发布文章使用:只允许注册用户才可以访问!

原创文章,版权所有©️xgqfrms, 禁止转载 🈲️,侵权必究⚠️!


posted @ 2022-11-16 23:45  xgqfrms  阅读(661)  评论(2编辑  收藏  举报