我经常在“类体系结构”之后编写功能组件,其中所有与该组件有关的功能都像在类中的方法一样写入其中。
例如,我这里有一个与counterAsFloat
组件有关的函数Counter
。如您所见,我只是将其写在组件内部:
export default function Counter() {
const [counter, setCounter] = React.useState(0);
const counterAsFloat = () => {
return counter.toFixed(2);
};
return (
<div className="counter">
<h1>{counterAsFloat()}</h1>
<button onClick={() => setCounter(counter + 1)}>
Increment
</button>
</div>
);
}
但是实际上我也可以在组件外部声明该函数,并将其与参数一起使用:
const counterAsFloat = (counter) => {
return counter.toFixed(2);
};
export default function Counter() {
const [counter, setCounter] = React.useState(0);
return (
<div className="counter">
<h1>{counterAsFloat(counter)}</h1>
<button onClick={() => setCounter(counter + 1)}>
Increment
</button>
</div>
);
}
那么在功能组件之外编写功能是否有优点或缺点?
答案 0 :(得分:10)
这个问题基于观点,但是您需要考虑的笔记很少。
在可读性和可重用性方面,首先声明超出范围的功能。
// Reuse logic in other components
const counterAsFloat = (counter) => {
return counter.toFixed(2);
};
// If Counter has complex logic, you sometimes want to compose it
// from functions to make it more readable.
export default function Counter() {
...
return (...);
}
有人认为第一个选项的性能较差,因为您在每个渲染器上都声明了该函数:
export default function Counter() {
...
// declare the function on every render
const counterAsFloat = () => {
return counter.toFixed(2);
};
return (...);
}
这种情况是premature optimization。查看与此相关的JavaScript closures performance。
请注意,在这种特定情况下,内联功能是更好的方法。
export default function Counter() {
...
return (
<div>
<h1>{counter.toFixed(2)}</h1>
...
</div>
);
}
答案 1 :(得分:1)
尽管您可能希望使用外部功能来进行组织或重用,但它似乎仍然与功能组件的结构背道而驰,至少出于以下一个原因:在功能组件中,状态是不可变的。因此,它们通常是常数。尽管您的两个功能似乎有些相似,但就此特定功能而言,它们却相差很大。以下面的代码为例:
const a = 2;
function increment(){
return ++a;
}
increment();
这显然是禁止的,不能更改常量。
用不同的方式写:
const a = 2;
function increment(a){
return ++a;
}
increment(a);
允许最后一个。它不会给出您期望的结果,至少要快速查看它,但是它将编译并且不会出现任何运行时错误。
将此转换为您的示例。假设您开始只是想简单地使用toFixed(2)输出您的计数器,然后创建一个外部函数。但是随后,您决定要重置5个以上的计数器。因此,您可以这样做:
const counterAsFloat = (counter) => {
if(counter > 5){
counter = 0;
}
return counter.toFixed(2);
};
这将被允许,将编译并运行。它不会给出预期的结果,但是不会很明显。内部函数可以工作:
const counterAsFloat = () => {
if(counter > 5){
counter = 0;
}
return counter.toFixed(2);
};
但是因为在内部作用域 counter 中是一个常量,所以您将出现编译错误或至少是运行时错误。您可以通过将counter = 0;
替换为setCounter(0);
来快速解决问题,这是处理此要求的正确方法。
因此,最后,通过留在组件内部,状态值将变得更加清晰,并且您将获得关于禁止操作的更清晰反馈,而对于外部函数来说,这种禁止可能不太明显。
请参阅带有外部函数的示例,它可以正常工作,但不能给您预期的结果:
const counterAsFloatOutside = (counter) => {
if(counter > 5){
counter = 0;
}
return counter.toFixed(2);
};
function Counter() {
const [counter, setCounter] = React.useState(0);
return (
<div className="counter">
<h1>{counterAsFloatOutside(counter)}</h1>
<button onClick={() => setCounter(counter + 1)}>
Increment
</button>
</div>
);
}
ReactDOM.render(React.createElement(Counter, null), document.body);
<script type="text/javascript" src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script type="text/javascript" src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
使用内部函数,它不起作用,在这种情况下,这是更可取的。使用任何编译工具甚至都可以为您带来错误,这是一个巨大的优势:
function Counter() {
const [counter, setCounter] = React.useState(0);
const counterAsFloat = () => {
if(counter > 5){
counter = 0;
}
return counter.toFixed(2);
};
return (
<div className="counter">
<h1>{counterAsFloat()}</h1>
<button onClick={() => setCounter(counter + 1)}>
Increment
</button>
</div>
);
}
ReactDOM.render(React.createElement(Counter, null), document.body);
<script type="text/javascript" src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script type="text/javascript" src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>