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 问题复现条件:
- state function component
有状态
函数组件 - state class component
有状态
函数组件 - hooks component
钩子
组件
pure react app tested ❌
- stateless function component
无状态
函数组件 - stateless class component
无状态
函数组件
Next.js 问题复现条件:
-
state function component
有状态
函数组件 -
state class component
有状态
函数组件 -
hooks component
钩子
组件 -
stateless function component
无状态
函数组件 -
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 性能优化
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
Function Component
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
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
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);
}
// ...
}
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);
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, 禁止转载 🈲️,侵权必究⚠️!
本文首发于博客园,作者:xgqfrms,原文链接:https://www.cnblogs.com/xgqfrms/p/16894519.html
未经授权禁止转载,违者必究!