简体   繁体   English

React hooks:将组件作为函数调用与作为元素渲染

[英]React hooks: call component as function vs render as element

Say we have component:假设我们有组件:

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

I have often come across code where someone calls a react component as function in render:我经常遇到这样的代码,其中有人在渲染中将 react 组件称为函数

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

vs rendering it as element vs将其渲染为元素

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

In react hooks, which is recommended practice?在反应钩子中,推荐的做法是什么?


There is similar question but it is not specifically targeted at hooks (also it is more about performance).类似的问题,但它不是专门针对钩子的(它更多的是关于性能)。 With hooks situation appears to be somewhat different and calling it as function is more anti pattern.使用钩子的情况似乎有些不同,将其称为函数更像是反模式。

TL DR ; TL DR ; In most of the cases you want <Component/>在大多数情况下你想要<Component/>


Here are some reasons:以下是一些原因:

  1. Potential violation of rules of hooks可能违反钩子规则

When you call a component as a function (see TestB() below) and it contains usage of hooks inside it , in that case react thinks the hooks within that function belongs to the parent component.当你调用一个组件作为一个功能(参见TestB()下图),它包含在它里面的钩子的使用,在这种情况下,反应认为钩的功能属于组件。 Now if you conditionally render that component (eg TestB() ) you will violate one of the rules of hooks.现在,如果您有条件地渲染该组件(例如TestB() ),您将违反钩子规则之一。 Check the example below, click the re-render button to see the error:检查下面的示例,单击重新渲染按钮以查看错误:

Error: Rendered fewer hooks than expected.错误:呈现的钩子比预期的少。 This may be caused by an accidental early return statement.这可能是由意外的提前退货声明引起的。

 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>

Now you can use <TestB/> instead and see the difference.现在您可以改用<TestB/>并查看不同之处。

  1. Reconciliation might not work as expected对帐可能无法按预期进行

When you render a react component as react element say <TestB/> and then on next render you render some different component <TestC/> instead of it (in the same place in component hierarchy), due to reconciliation algorithm (and since component type has changed ), react will unmount <TestB/> component (all its state will be gone) and mount a new component <TestC/> instead.当您将反应组件渲染为反应元素时说<TestB/>然后在下一次渲染时渲染一些不同的组件<TestC/>而不是它(在组件层次结构中的同一位置),由于协调算法(并且由于组件类型has changed ),react 将卸载<TestB/>组件(它的所有状态都将消失)并安装一个新组件<TestC/>代替。

If you call it as function however (eg TestB() ), the component type will not participate in reconciliation anymore and you might not get expected results:但是,如果您将其称为函数(例如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>

  • Type something in the input在输入中输入一些东西
  • Now click the re-render button现在单击重新渲染按钮
  • You can see now from the log that component TestC was rendered, but the input shows the same value you typed before (which might not be what you want as you rendered a different component).您现在可以从日志中看到组件TestC已呈现,但输入显示的值与您之前键入的值相同(当您呈现不同的组件时,这可能不是您想要的)。 This happened because reacts reconciliation algorithm couldn't detect that we moved to a different component (from TestB to TestC ) and didn't remove previous input instance from DOM.发生这种情况是因为反应协调算法无法检测到我们移动到不同的组件(从TestBTestC )并且没有从 DOM 中删除以前的输入实例。

Render these components as elements now ( <TestB/> and <TestC/> ) to see the difference.现在将这些组件渲染为元素( <TestB/><TestC/> )以查看差异。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM