Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[React] react组件重复渲染 #216

Open
david2tdw opened this issue Mar 15, 2022 · 12 comments
Open

[React] react组件重复渲染 #216

david2tdw opened this issue Mar 15, 2022 · 12 comments

Comments

@david2tdw
Copy link
Owner

深入 React 函数组件的 re-render 原理及优化

@david2tdw
Copy link
Owner Author

组件本身使用 useState 或 useReducer 更新,引起的 re-render

常规使用:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>React template</title>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <style></style>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      const { useState } = React;
      const Counter = () => {
        console.log("counter render");
        const [count, addCount] = useState(0);

        return (
          <div>
            <div>{count}</div>
            <button
              onClick={() => {
                addCount(count + 1);
              }}
            >
              add
            </button>
          </div>
        );
      };

      ReactDOM.render(<Counter />, document.getElementById("root"));
    </script>
  </body>
</html>

@david2tdw
Copy link
Owner Author

immutation state

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>immutation state</title>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <style></style>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      const { useState } = React;
      const Counter = () => {
        console.log("counter render");
        //我们将上面计数组件中的 state 值改成引用类型, 点击并不会引起 re-render
        const [count, addCount] = useState({
          num: 0,
          time: Date.now(),
        });

        const handleClick = () => {
          count.num++;
          count.time = Date.now();
          addCount(count);
        };
        return (
          <div>
            <div>
              {count.num}, {count.time}
            </div>
            <button onClick={handleClick}>add</button>
          </div>
        );
      };

      ReactDOM.render(<Counter />, document.getElementById("root"));
    </script>
  </body>
</html>

@david2tdw
Copy link
Owner Author

强制更新

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>强制更新</title>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <style></style>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      const { useState } = React;
      const Counter = () => {
        console.log("counter render");
        const [, forceUpdate] = useState({});
        //我们将上面计数组件中的 state 值改成引用类型
        const [count, addCount] = useState({
          num: 0,
          time: Date.now(),
        });

        const handleClick = () => {
          count.num++;
          count.time = Date.now();
          addCount(count);
          forceUpdate({});
        };
        return (
          <div>
            <div>
              {count.num}, {count.time}
            </div>
            <button onClick={handleClick}>add</button>
          </div>
        );
      };

      ReactDOM.render(<Counter />, document.getElementById("root"));
    </script>
  </body>
</html>

@david2tdw
Copy link
Owner Author

父组件更新引起子组件的 re-render
常规使用

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>常规使用</title>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <style></style>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      const { useState } = React;

      const Hello = ({ name }) => {
        console.log("hello render");
        return <div>hello {name}</div>;
      };
      const App = () => {
        console.log("app render");
        const [count, addCount] = useState(0);

        const handleClick = () => {
          addCount(count + 1);
        };
        return (
          <div>
            <Hello name="react" />
            <button onClick={handleClick}>add</button>
          </div>
        );
      };

      ReactDOM.render(<App />, document.getElementById("root"));
    </script>
  </body>
</html>

@david2tdw
Copy link
Owner Author

父组件更新引起子组件的 re-render - 优化组件设计
将更新部分抽离成单独组件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>将更新部分抽离成单独组件</title>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <style></style>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      const { useState } = React;

      const Counter = () => {
        console.log("counter render");
        const [count, addCount] = useState(0);

        const handleClick = () => {
          addCount(count + 1);
        };

        return <button onClick={handleClick}>add</button>;
      };

      const Hello = ({ name }) => {
        console.log("hello render");
        return <div>hello {name}</div>;
      };

      const App = () => {
        console.log("app render");

        return (
          <div>
            <Hello name="react" />
            <Counter />
          </div>
        );
      };

      ReactDOM.render(<App />, document.getElementById("root"));
    </script>
  </body>
</html>

@david2tdw
Copy link
Owner Author

将不需要 re-render 的部分抽离,以插槽形式渲染(children)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>将不需要 re-render 的部分抽离,以插槽形式渲染</title>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <style></style>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      const { useState } = React;

      const Hello = ({ name }) => {
        console.log("hello render");
        return <div>hello {name}</div>;
      };
      const App = ({ children }) => {
        console.log("app render");
        const [count, addCount] = useState(0);

        const handleClick = () => {
          addCount(count + 1);
        };
        return (
          <div>
            {children}
            <button onClick={handleClick}>add</button>
          </div>
        );
      };

      const Main = () => {
        return (
          <App>
            <Hello name="react" />
          </App>
        );
      };
      ReactDOM.render(<Main />, document.getElementById("root"));
    </script>
  </body>
</html>

@david2tdw
Copy link
Owner Author

React.memo

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>React.memo</title>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <style></style>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      const { useState } = React;

      const Hello = React.memo(({ name }) => {
        console.log("hello render");
        return <div>hello {name}</div>;
      });

      const App = () => {
        console.log("app render");
        const [count, addCount] = useState(0);

        const handleClick = () => {
          addCount(count + 1);
        };
        return (
          <div>
            <Hello name="react" />
            <button onClick={handleClick}>add</button>
          </div>
        );
      };

      ReactDOM.render(<App />, document.getElementById("root"));
    </script>
  </body>
</html>

@david2tdw
Copy link
Owner Author

React.memo 引用类的 props, 添加事件处理的函数

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>React.memo 引用类的 props, 添加事件处理的函数</title>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <style></style>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      const { useState } = React;

      const Hello = React.memo(({ name, onClick }) => {
        console.log("hello render");
        return <div onClick={onClick}>hello {name}</div>;
      });

      const App = () => {
        console.log("app render");
        const [count, addCount] = useState(0);

        const handleClick = () => {
          console.log("hello click");
        };
        return (
          <div>
            <Hello name="react" onClick={handleClick} />
            <button
              onClick={() => {
                addCount(count + 1);
              }}
            >
              add
            </button>
          </div>
        );
      };

      ReactDOM.render(<App />, document.getElementById("root"));
    </script>
  </body>
</html>

@david2tdw
Copy link
Owner Author

useCallback

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>useCallback</title>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <style></style>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      const { useState, useCallback } = React;

      const Hello = React.memo(({ name, onClick }) => {
        console.log("hello render");
        return <div onClick={onClick}>hello {name}</div>;
      });

      const App = () => {
        console.log("app render");
        const [count, addCount] = useState(0);

        const handleClick = useCallback(() => {
          console.log("hello click");
        }, []);
        
        return (
          <div>
            <Hello name="react" onClick={handleClick} />
            <button
              onClick={() => {
                addCount(count + 1);
              }}
            >
              add
            </button>
          </div>
        );
      };

      ReactDOM.render(<App />, document.getElementById("root"));
    </script>
  </body>
</html>

@david2tdw
Copy link
Owner Author

useCallback use state

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>useCallback use state</title>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <style></style>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      const { useState, useCallback } = React;

      const Hello = React.memo(({ name, onClick }) => {
        console.log("hello render");
        return <div onClick={onClick}>hello {name}</div>;
      });

      const App = () => {
        console.log("app render");
        const [count, addCount] = useState(0);

        const handleClick = useCallback(() => {
          console.log("hello click count" , count);
        }, []);
        
        return (
          <div>
            <Hello name="react" onClick={handleClick} />
            <button
              onClick={() => {
                addCount(count + 1);
              }}
            >
              add
            </button>
            <div>count is: {count}</div>
          </div>
        );
      };

      ReactDOM.render(<App />, document.getElementById("root"));
    </script>
  </body>
</html>

@david2tdw
Copy link
Owner Author

david2tdw commented Mar 16, 2022

useCallback use state fix - 会导致闭包问题,子组件重复渲染

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>useCallback use state fix</title>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <style></style>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      const { useState, useCallback } = React;

      const Hello = React.memo(({ name, onClick }) => {
        console.log("hello render");
        return <div onClick={onClick}>hello {name}</div>;
      });

      const App = () => {
        console.log("app render");
        const [count, addCount] = useState(0);

        const handleClick = useCallback(() => {
          console.log("hello click count" , count);
        }, [count]);
        
        return (
          <div>
            <Hello name="react" onClick={handleClick} />
            <button
              onClick={() => {
                addCount(count + 1);
              }}
            >
              add
            </button>
            <div>count is: {count}</div>
          </div>
        );
      };

      ReactDOM.render(<App />, document.getElementById("root"));
    </script>
  </body>
</html>

@david2tdw
Copy link
Owner Author

useRef & useEffect

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>useRef & useEffect</title>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <style></style>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      const { useState, useCallback, useRef, useEffect } = React;

      const Hello = React.memo(({ name, onClick }) => {
        console.log("hello render");
        return <div onClick={onClick}>hello {name}</div>;
      });

      const App = () => {
        console.log("app render");
        const [count, addCount] = useState(0);

        const countRef = useRef(count);

        useEffect(() => {
          countRef.current = count;
        }, [count]);

        const handleClick = useCallback(() => {
          console.log("hello click count", countRef.current);
        }, [countRef]);

        return (
          <div>
            <Hello name="react" onClick={handleClick} />
            <button
              onClick={() => {
                addCount(count + 1);
              }}
            >
              add
            </button>
            <div>count is: {count}</div>
          </div>
        );
      };

      ReactDOM.render(<App />, document.getElementById("root"));
    </script>
  </body>
</html>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant