【问题标题】:React hooks: call component as function vs render as elementReact hooks:将组件调用为函数与渲染为元素
【发布时间】:2021-11-24 15:52:23
【问题描述】:

假设我们有组件:

let Component = (props)=><div>Hi</div>;

我有时会遇到有人在渲染中将反应组件调用为函数的代码:

 const App = () => (
     <div> {Component()} </div>
 )

vs 将其渲染为元素

 const App = () => (
     <div> <Component/> </div>
 )

在 react 钩子中,将组件调用为函数可能有哪些缺点?


similar 的问题,但它并不是专门针对钩子的——而且这个问题更多的是关于性能。

【问题讨论】:

    标签: javascript reactjs react-hooks


    【解决方案1】:

    以下是将组件调用为函数与将其呈现为元素的一些含义。

    1. 可能违反钩子规则

    当您将组件调用为函数(参见下面的TestB())并且它包含钩子的用法在其中,在这种情况下,react 认为钩子该函数属于组件。现在,如果您有条件地渲染该组件 (TestB()),您将违反挂钩中的 rules 之一。查看下面的示例,点击重新渲染按钮查看错误:

    错误:渲染的钩子比预期的少。这可能是由一个 意外提前退货声明。

     
    function TestB() {
      let [B, setB] = React.useState(0);
      return (
        <div
          onClick={() => {
            setB(B + 1);
          }}
        >
          counter B {B}
        </div>
      );
    }
     
    
    function App() {
      let [A, setA] = React.useState(0);
    
      return (
        <div>
          <button
            onClick={() => {
              setA(A + 1);
            }}
          >
            re-render
          </button>
          {/* Conditionally render TestB() */}
          {A % 2 == 0 ? TestB() : null}
        </div>
      );
    }
    ReactDOM.render(
      <App />,
      document.getElementById("react")
    );
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
    <div id="react"></div>

    现在您可以改用&lt;TestB/&gt; 并查看区别。

    1. Reconciliation 可能无法按预期工作

    当你渲染一个反应组件作为反应元素说&lt;TestB/&gt;然后在下一次渲染你渲染一些不同的组件&lt;TestC/&gt;而不是它(在组件层次结构中的同一位置),由于协调算法(并且因为组件类型已更改),react 将卸载 &lt;TestB/&gt; 组件(其所有状态将消失)并安装一个新组件 &lt;TestC/&gt;

    如果您将其作为函数调用(例如TestB()),组件类型将不再参与协调,您可能不会得到预期的结果:

    function TestB() {    
      return (
        <div     
        >
          <input/>
        </div>
      );
    }
    function TestC() {
      console.log("TestC")
      return (
        <div     
        >
          <input/>
        </div>
      );
    }
    
    function App() {
      let [A, setA] = React.useState(0);
    
      return (
        <div>
          <button
            onClick={() => {
              setA(A + 1);
            }}
          >
            re-render
          </button>
          {/*  Here we are alternating rendering of components */}
          {A % 2 == 0 ? TestB() : TestC()} 
        </div>
      );
    }
    
    ReactDOM.render(
      <App />,
      document.getElementById("react")
    );
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
        <div id="react"></div>
    • 在输入中输入一些内容
    • 现在点击重新渲染按钮
    • 您现在可以从日志中看到组件TestC 已渲染,但输入显示的值与您之前键入的值相同——这可能不是您想要的,因为您渲染了不同的组件。发生这种情况是因为 reacts 协调算法无法检测到我们移动到了一个不同的组件(从 TestBTestC)并且没有从 DOM 中删除之前的输入实例。

    现在将这些组件渲染为元素(&lt;TestB/&gt;&lt;TestC/&gt;)以查看差异。

    【讨论】:

      猜你喜欢
      • 2019-07-06
      • 1970-01-01
      • 2021-07-02
      • 1970-01-01
      • 2018-12-08
      • 2018-03-07
      • 2019-07-08
      • 2020-07-07
      • 2020-02-05
      相关资源
      最近更新 更多