Next.js & React component render twice bug All In One
<h1 id="nextjs--react-component-render-twice-bug-all-in-one">Next.js & React component render twice bug All In One</h1><h2 id="error-">error ❌</h2>
<p><img src="https://img2022.cnblogs.com/blog/928074/202211/928074-20221115234753222-1993873685.png" alt="" loading="lazy"></p>
<blockquote>
<p><code>React.StrictMode</code></p>
</blockquote>
<p>https://github.com/facebook/react/issues/15074</p>
<p>https://reactjs.org/docs/strict-mode.html</p>
<blockquote>
<p>New <code>Strict Mode</code> Behaviors</p>
</blockquote>
<p>https://reactjs.org/blog/2022/03/29/react-v18.html#new-strict-mode-behaviors</p>
<h2 id="solution-">solution ✅</h2>
<pre><code class="language-diff">// next.config.js
module.exports = {
-reactStrictMode: true,
+reactStrictMode: false,
}
</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/928074/202211/928074-20221115235700643-1725242458.png" alt="" loading="lazy"></p>
<p>https://nextjs.org/docs/api-reference/next.config.js/react-strict-mode</p>
<h2 id="demos">demos</h2>
<blockquote>
<p>React 问题复现条件:</p>
</blockquote>
<ol>
<li>state function component<code>有状态</code>函数组件</li>
<li>state class component<code>有状态</code>函数组件</li>
<li>hooks component <code>钩子</code>组件</li>
</ol>
<p>pure react app tested ❌</p>
<ol start="4">
<li>stateless function component<code>无状态</code>函数组件</li>
<li>stateless class component<code>无状态</code>函数组件</li>
</ol>
<blockquote>
<p>Next.js 问题复现条件:</p>
</blockquote>
<ol>
<li>
<p>state function component<code>有状态</code>函数组件</p>
</li>
<li>
<p>state class component<code>有状态</code>函数组件</p>
</li>
<li>
<p>hooks component <code>钩子</code>组件</p>
</li>
<li>
<p>stateless function component<code>无状态</code>函数组件</p>
</li>
<li>
<p>stateless class component<code>无状态</code>函数组件</p>
</li>
</ol>
<pre><code class="language-jsx">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
*/
</code></pre>
<h2 id="react-component-性能优化">React Component 性能优化</h2>
<ol>
<li><code>PureComponent</code> ✅</li>
</ol>
<pre><code class="language-jsx">import React,{PureComponent}from "react";
class Welcome extends React.PureComponent {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
</code></pre>
<p>https://reactjs.org/docs/render-props.html#caveats</p>
<pre><code class="language-jsx">
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>
);
}
}
</code></pre>
<p>https://reactjs.org/docs/react-api.html#reactpurecomponent</p>
<ol start="2">
<li><code>Function Component</code></li>
</ol>
<pre><code class="language-jsx">function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
</code></pre>
<ol start="3">
<li><code>Class Component</code></li>
</ol>
<pre><code class="language-jsx">import React,{Component}from "react";
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
</code></pre>
<p>https://reactjs.org/docs/components-and-props.html</p>
<p><img src="https://img2022.cnblogs.com/blog/740516/202211/740516-20221117002837197-1616702393.png" alt="image" loading="lazy"></p>
<p>Since <code>shouldComponentUpdate</code> returned <code>false </code>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.</p>
<p>For C1 and C3, <code>shouldComponentUpdate</code> returned <code>true</code>, so React had to go down to the leaves and check them.<br>
For C6 <code>shouldComponentUpdate</code> returned <code>true</code>, and since the <code>rendered elements</code> weren’t equivalent React had to update the DOM.</p>
<p>The last interesting case is <code>C8</code>.<br>
React had to render this component, but since the <code>React elements </code>it returned were equal to the previously rendered ones, it didn’t have to update the DOM.</p>
<p>Note that React only had to do <code>DOM mutations</code> for C6, which was inevitable.<br>
For C8, it <code>bailed out</code> by comparing the rendered <code>React elements</code>,<br>
and for C2’s subtree and C7, it didn’t even have to compare the elements as we <code>bailed out</code> on <code>shouldComponentUpdate</code>, and render was not called.</p>
<pre><code class="language-jsx">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>;
}
}
</code></pre>
<p>https://reactjs.org/docs/optimizing-performance.html#shouldcomponentupdate-in-action</p>
<h2 id="blogs">blogs</h2>
<p>Extending from <code>React.PureComponent</code> or from <code>React.Component</code> with a custom <code>shouldComponentUpdate</code> method have performance implications.</p>
<pre><code class="language-jsx">class CustomPureComponent extends Component {
shouldComponentUpdate (nextProps, nextState) {
return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
}
// ...
}
</code></pre>
<p>https://stackoverflow.com/questions/40703675/react-functional-stateless-component-purecomponent-component-what-are-the-dif</p>
<blockquote>
<p><code>HOC</code></p>
</blockquote>
<p>higher-order components</p>
<p>https://reactjs.org/docs/higher-order-components.html</p>
<p>https://github.com/acdlite/recompose</p>
<p>https://github.com/acdlite/recompose/blob/master/docs/API.md#pure</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/Cla1WwguArA?start=1" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<pre><code class="language-jsx">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);
</code></pre>
<p><code>React.memo</code></p>
<pre><code class="language-jsx">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);
</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/740516/202211/740516-20221117020219894-1529376027.png" alt="" loading="lazy"></p>
<p><code>arePropsEqual</code></p>
<pre><code class="language-jsx">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);
</code></pre>
<p>https://blog.logrocket.com/what-are-react-pure-functional-components/#are-react-functional-components-pure</p>
<h2 id="refs">refs</h2>
<p>https://github.com/web-full-stack/nextjs-ssr/issues/6#issuecomment-1315425580</p>
<blockquote>
<p>next.js react components render twice bug ❌</p>
</blockquote>
<p>https://deniapps.com/blog/why-the-react-component-renders-twice</p>
<p>https://www.heissenberger.at/en/blog/react-components-reder-twice/</p>
<p>https://mariosfakiolas.com/blog/my-react-components-render-twice-and-drive-me-crazy/</p>
<hr>
<div>
</div>
<hr>
<blockquote style="display: flex; flex-flow: column; align-items: center; justify-content: center; text-align: center; border: none">
<h3><strong><span style="font-size: 16pt; color: rgba(0, 255, 0, 1)">©xgqfrms 2012-<span data-uid="copyright-aside">2021</span></span></strong>
<p><span style="font-size: 18pt; color: rgba(0, 255, 0, 1)"><strong>www.cnblogs.com/xgqfrms 发布文章使用:只允许注册用户才可以访问!</strong></span></p>
<p><span style="font-size: 18pt; color: rgba(0, 255, 0, 1)"><strong>原创文章,版权所有©️xgqfrms, 禁止转载 🈲️,侵权必究⚠️!</strong></span></p>
</h3></blockquote>
<hr>
</div>
<div id="MySignature" role="contentinfo">
<div style="display: flex; flex-flow: column nowrap; align-items: center; justify-content: center;">
<p>本文首发于博客园,作者:xgqfrms,原文链接:https://www.cnblogs.com/xgqfrms/p/16894519.html</p>
<p style="color: red; font-size: 23px; margin-top: 5px; margin-botom: 5px;">未经授权禁止转载,违者必究!</P>
</div>
<hr/><br><br>
来源:https://www.cnblogs.com/xgqfrms/p/16894519.html
頁:
[1]