React hooks:将组件作为函数调用与作为元素渲染 [英] React hooks: call component as function vs render as element
问题描述
假设我们有组件:
let Component = (props)=>Hi;
我有时会遇到这样的代码,其中有人在渲染中将反应组件作为函数调用:
const App = () =>(<div>{Component()} </div>)
vs 将其渲染为元素
const App = () =>(<div><组件/>
)
在 React 钩子中,将组件作为函数调用有哪些可能的缺点?
有类似的问题,但它是不是专门针对钩子的 - 这个问题更多地是关于性能的.
以下是将组件作为函数调用与将其渲染为元素的一些含义.
- 可能违反钩子规则
当你将一个组件作为函数调用(参见下面的TestB()
)并且它包含内部的钩子使用,在那个case react 认为 函数内的钩子属于 父 组件.现在,如果您有条件地呈现该组件 (TestB()
),您将违反 规则 钩子.检查下面的示例,单击重新渲染按钮查看错误:
错误:呈现的钩子比预期的少.这可能是由意外提前退货声明.
功能测试B(){让 [B, setB] = React.useState(0);返回 ({设置B(B + 1);}}>计数器 B {B}
);}功能应用(){让 [A, setA] = React.useState(0);返回 (<div><按钮onClick={() =>{设置A(A + 1);}}>重新渲染按钮>{/* 有条件地渲染 TestB() */}{A % 2 == 0 ?测试B():空}
);}ReactDOM.render(<应用程序/>,document.getElementById("反应"));
<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>
现在您可以改用
并查看不同之处.
- 协调可能无法按预期进行
当你将一个 react 组件渲染为 react 元素时,说
然后在下一次渲染时你渲染一些不同的组件
其中(在组件层次结构中的同一位置),由于协调算法(并且由于组件类型已更改),react 将卸载
组件(它的所有状态都将消失)并安装一个新组件
代替.
如果您将其称为函数(例如 TestB()
),组件类型将不再参与协调,您可能无法获得预期的结果:>
function TestB() {返回 ();}函数 TestC() {console.log("TestC")返回 (
);}功能应用(){让 [A, setA] = React.useState(0);返回 (<div><按钮onClick={() =>{设置A(A + 1);}}>重新渲染按钮>{/* 这里我们交替渲染组件 */}{A % 2 == 0 ?TestB() : TestC()}
);}ReactDOM.render(<应用程序/>,document.getElementById("反应"));
<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>
- 在输入中输入内容
- 现在点击重新渲染按钮
- 您现在可以从日志中看到组件
TestCcode> 已呈现,但输入显示的值与您之前键入的值相同 - 当您呈现不同的组件时,这可能不是您想要的.发生这种情况是因为 reacts 协调算法无法检测到我们移动到了不同 组件(从
TestB
到TestCcode>)并且没有删除以前的来自 DOM 的输入实例.
现在将这些组件渲染为元素(
和
)以查看差异.
Say we have component:
let Component = (props)=><div>Hi</div>;
I have sometimes come across code where someone calls a react component as function in render:
const App = () => (
<div> {Component()} </div>
)
vs rendering it as element
const App = () => (
<div> <Component/> </div>
)
In react hooks, what are possible drawbacks of calling component as function?
There is similar question but it is not specifically targeted at hooks - also that question is more about performance.
Here are some implications of calling component as function vs rendering it as element.
- 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. Now if you conditionally render that component (TestB()
) you will violate one of the rules of hooks. 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.
- 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.
If you call it as function however (e.g. TestB()
), the component type will not participate in reconciliation anymore and you might not get expected results:
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. This happened because reacts reconciliation algorithm couldn't detect that we moved to a different component (fromTestB
toTestC
) and didn't remove previous input instance from DOM.
Render these components as elements now (<TestB/>
and <TestC/>
) to see the difference.
这篇关于React hooks:将组件作为函数调用与作为元素渲染的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!