Функциональный компонент: писать функции внутри или вне компонента? - PullRequest
12 голосов
/ 11 июля 2020

Я часто писал функциональные компоненты, следуя «архитектуре классов», где все мои функции, относящиеся к компоненту, записываются внутри него, как метод в классе.

Например, у меня есть функция 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 ]

7 голосов
/ 11 июля 2020
• 1000 Можно утверждать, что первый вариант менее эффективен, потому что вы объявляете функцию при каждом рендеринге:
export default function Counter() {
  ...

  // declare the function on every render
  const counterAsFloat = () => {
    return counter.toFixed(2);
  };

  return (...);
}

Такой случай - преждевременная оптимизация . Проверьте JavaScript производительность закрытия , которая относится к этому.

Обратите внимание, что в этом конкретном случае c встраивание функция намного лучше.

export default function Counter() {
  ...
  return (
    <div>
      <h1>{counter.toFixed(2)}</h1>
      ...
    </div>
  );
}
1 голос
/ 16 июля 2020

Хотя вы можете захотеть использовать внешние функции для организации или повторного использования, это, похоже, go противоречит структуре функциональных компонентов, по крайней мере, по одной причине: в функциональных компонентах состояния неизменяемы. Так что обычно они постоянные. И хотя ваши две функции кажутся чем-то похожими, они сильно различаются именно в отношении этой c особенности. Возьмем для примера этот код:

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>
...