功能组件:在组件内部还是外部编写函数?

时间:2020-07-11 10:41:16

标签: javascript reactjs react-hooks

我经常在“类体系结构”之后编写功能组件,其中所有与该组件有关的功能都像在类中的方法一样写入其中。

例如,我这里有一个与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>
  );
}

那么在功能组件之外编写功能是否有优点或缺点?

2 个答案:

答案 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>