Различия между функциональными компонентами и компонентами класса в React.

Когда я начинаю свой проект React, мой первый вопрос заключается в том, использовать ли компоненты класса или функциональные компоненты. Другой вариант — использовать оба в проекте. Надеюсь, каждый использует оба компонента в своих проектах.

React позволяет определять компоненты как классы или функции.

Прежде чем найти лучший вариант, мы собираемся создать компоненты класса и функциональные компоненты.

При создании компонента React его имя ДОЛЖНО начинаться с заглавной буквы.

Компонент класса

Чтобы определить класс компонента React, ваш класс должен быть расширен с помощью React.Component.

Метод render() должен быть определен в компоненте класса. Другие методы React.Component являются необязательными, например constructor() componentDidMount() и т. д.

class Hello extends React.Component {
  render() {
    return <h1>Hello World!</h1>;
  }
}
  
const element = <Hello />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

Попробуй на CodePen

Функциональный компонент

Здесь мы собираемся изменить вышеуказанный компонент класса на функциональный компонент.

function Hello() {
  return <h1>Hello World!</h1>;
}
  
const element = <Hello />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

Функциональные компоненты легко написать с меньшим количеством кода и легче понять. Потому что это обычная функция JavaScript, которая возвращает JSX.

Попробуй на CodePen

Различия между функциональными компонентами и компонентами класса

Мы рассмотрим каждое различие между компонентами класса и функциональными компонентами.

  • 1. Визуализация компонентов
  • 2. Работа с реквизитом
  • 3. Обработка состояний
  • 4. Методы жизненного цикла
  • 5. Доступ к дочерним компонентам
  • 6. Компоненты высшего порядка
  • 7. Границы ошибки

1. Визуализация компонентов

В компоненте класса метод render() используется для рендеринга JSX путем расширения React.Component

Функциональный компонент — это обычная функция JavaScript, которая возвращает JSX.

Первые две программы являются прекрасным примером рендеринга компонентов.

// Class component
class Hello extends React.Component {
  render() {
    return <h1>Hello World!</h1>;
  }
}
  
//Function component
function Hello() {
  return <h1>Hello World!</h1>;
}
  
//Function component with Arrow function
Hello = () => {
  return <h1>Hello World!</h1>;
}

Победитель: функциональный компонент

Создание функционального компонента упрощается с помощью стрелочной функции. В качестве функционального компонента используется простая функция JavaScript. Нет больше метода рендеринга.



2. Работа с реквизитом

props обозначает свойства и передается в компонент React. Он также используется для передачи данных от одного компонента к другому. Подробнее читайте в официальной Документации React.

Компонент класса с реквизитами

this.props используется для доступа к нашим name реквизитам.

class Hello extends React.Component {
  render() {
    return <h1>Hello {this.props.name}!</h1>;
  }
}
  
const element = <Hello name="World"/>;
ReactDOM.render(
  element,
  document.getElementById('root')
);

Попробуй на CodePen

Функциональный компонент с реквизитами

Действительный функциональный компонент React должен иметь один аргумент для функции. Аргумент props будет иметь все реквизиты компонента, такие как props.name.

function Hello(props) {
  return <h1>Hello {props.name}!</h1>;
}
   
const element = <Hello name="World" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

Попробуй на CodePen

Победитель: функциональный компонент

Не беспокойтесь о ключевом слове this. Синтаксис чистый и простой, это победитель.

3. Состояние обработки

state — это встроенный объект компонента React, он используется для управления поведением компонента. Когда объект state изменится, компонент будет повторно визуализирован.

Компонент класса с состоянием

class Timenow extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }
  
  render() {
    return(
      <div>
        <h1>Hello {this.props.name}!</h1>
        <h2>Time Now {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}
  
const element = <Timenow name="World"/>;
ReactDOM.render(
  element,
  document.getElementById('root')
);

Попробуй на CodePen

Метод компонентов класса constructor используется для инициализации значений состояния.

Функциональный компонент с состоянием

В функциональном компоненте состояние обрабатывается с помощью хука useState.

Хуки — это новое дополнение к React 16.8. Они позволяют вам использовать состояние и другие функции React без написания класса.

Функциональный компонент не имеет this, поэтому мы не можем присвоить или прочитать this.state. Вместо этого мы вызываем хук useState непосредственно внутри нашего функционального компонента.

function Timenow(props) {
  const [date, setDate] = React.useState(new Date());
  return (
    <div>
      <h1>Hello {props.name}!</h1>
      <h2>Time Now {date.toLocaleTimeString()}.</h2>
    </div>
  );
}
  
const element = <Timenow name="World" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

Попробуй на CodePen

Победитель: функциональный компонент

Инициализация state выполняется с помощью хука useState. Метод constructor не требуется.

4. Методы жизненного цикла

В React каждый компонент имеет несколько методов lifecycle, этот метод помогает вам запускать код в определенное время в процессе. Нажмите здесь, чтобы просмотреть схему жизненного цикла React.

Компонент класса с методами жизненного цикла

Приведенный ниже компонент класса часов является прекрасным примером реализации методов жизненного цикла.

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }
  
  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }
  
  componentWillUnmount() {
    clearInterval(this.timerID);
  }
  
  tick() {
    this.setState({
      date: new Date()
    });
  }
  
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}
  
ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

Попробуй на CodePen

Функциональный компонент с методами подключения жизненного цикла

Мы преобразовали вышеуказанный компонент класса clock в функциональный компонент с помощью хука useEffect.

Метод возврата useEffect используется для очистки.

function Clock(props) {
  const [date, setDate] = React.useState(new Date());
  
  React.useEffect(() => {
    var timerID = setInterval(() => tick(), 1000);
    
    return function cleanup() {
      clearInterval(timerID);
    };
  });
    
  function tick() {
    setDate(new Date());
  }
  
  return (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {date.toLocaleTimeString()}.</h2>
    </div>
  );
}
  
ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

Попробуй на CodePen

Победитель: Компонент класса

Функциональный компонент useEffect сбивает с толку из-за использования одного и того же хука для всех методов жизненного цикла. В компоненте класса мы можем напрямую использовать методы componentDidMount, componentWillUnmount и т.д.

5. Доступ к дочерним компонентам

Специальная реквизита children используется для доступа к компоненту внутри контента или компоненту, например ‹Layout›внутри контента‹/Layout›.

Компонент класса

this.props.children используется для компонентов класса.

class Layout extends React.Component {
  render() {
   return(
      <div>
        <h1>Hello {this.props.name}!</h1>
        <div>{this.props.children}</div>
      </div>
     );
  }
}
  
const element = <Layout name="World">This is layout content</Layout>;
ReactDOM.render(
  element,
  document.getElementById('root')
);

Попробуй на CodePen

Функциональный компонент

Просто props.children используется в функциональных компонентах для доступа к дочернему контенту.

function Layout(props) {
  return(
    <div>
      <h1>Hello {props.name}!</h1>
      <div>{props.children}</div>
    </div>
  );
}
  
const element = <Layout name="World">This is layout content</Layout>;
ReactDOM.render(
  element,
  document.getElementById('root')
);

Попробуй на CodePen

Победитель: класс и функциональный компонент

Оба используют один и тот же props.children для доступа к дочерним компонентам. Кроме того, нам нужно использовать ключевое слово this.

6. Компоненты высшего порядка

Компонент высшего порядка (HOC) — это продвинутая техника в React для повторного использования логики компонентов. HOC — это чистая функция, поэтому она возвращает только новый компонент.

Компонент высшего порядка — это функция, которая принимает компонент и возвращает новый компонент.

HOC с компонентом класса

function classHOC(WrappedComponent) {
  return class extends React.Component{
    render() {
      return <WrappedComponent {...this.props}/>;
    }
  }
}
  
const Hello = ({ name }) => <h1>Hello {name}!</h1>;
const NewComponent = classHOC(Hello);
  
const element = <NewComponent name="World" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

Попробуй на CodePen

HOC с функциональным компонентом

function functionalHOC(WrappedComponent) {
  return (props) => {
    return <WrappedComponent {...props}/>;
  }
}
  
const Hello = ({ name }) => <h1>Hello {name}!</h1>;
const NewComponent = functionalHOC(Hello);
  
const element = <NewComponent name="World" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

Попробуй на CodePen

Победитель: функциональный компонент

Чтобы создать HOC, мы должны использовать функцию JavaScript (classHOC, functionHOC). Не используйте HOC внутри метода рендеринга. Внутри функции мы можем использовать класс или функциональные компоненты.

7. Границы ошибки

Границы ошибок — это компоненты React, используемые для обработки ошибок JavaScript в компонентах React. Таким образом, мы можем отлавливать ошибки времени выполнения JavaScript в наших компонентах и ​​отображать резервный пользовательский интерфейс.

Границы ошибок — это компоненты React, которые перехватывают ошибки JavaScript в любом месте своего дочернего дерева компонентов, регистрируют эти ошибки и отображают резервный пользовательский интерфейс вместо дерева компонентов, в котором произошел сбой.

Использование методов жизненного цикла static getDerivedStateFromError() или componentDidCatch() означает, что компонент класса становится границей ошибки. Используйте static getDerivedStateFromError() для отображения резервного пользовательского интерфейса после возникновения ошибки. Используйте componentDidCatch() для регистрации информации об ошибке.

Границы ошибок работают как блок JavaScript catch {}, но для компонентов.

Простой пример с границами ошибок

class ErrorCounter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {error: null, errorInfo: null};
  }
  
  componentDidCatch(error, errorInfo) {
    // Catch errors in any components below and re-render with error message
    this.setState({
      error: error,
      errorInfo: errorInfo
    })
    // You can also log error messages to an error reporting service here
  }
  
  refreshPage() {
    history.go(-1)
  }
  
  render() {
    if (this.state.errorInfo) {
      // Error path
      return (
        <div>
          <h2>Something went wrong.</h2>
          <details style={{ whiteSpace: 'pre-wrap' }}>
            {this.state.error && this.state.error.toString()}
            <br />
            {this.state.errorInfo.componentStack}
          </details>
          <hr />
          <button onClick={this.refreshPage}>
            Refresh Page
          </button>
        </div>
      );
    }
    // Normally, just render children
    return this.props.children;
  }
};
class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { counter: 0 };
    this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick() {
    this.setState(({counter}) => ({
      counter: counter + 1
    }));
  }
  
  render() {
    if (this.state.counter === 3) {
      // Simulate a JS error
      throw new Error('I crashed!');
    }
    return(
      <div>
        <h1>{this.state.counter}</h1>
        <button onClick={this.handleClick}>+</button>
      </div>
    );
  }
}
  
ReactDOM.render(
  <ErrorCounter>
    <Counter />
  </ErrorCounter>,
  document.getElementById('root')
);

Попробуй на CodePen

Победитель: Компонент класса

В настоящее время только компоненты класса могут быть границами ошибок.

Вывод: что лучше?

В настоящее время большинство приложений используют функциональные компоненты из-за их простоты, простоты использования и понимания. Также функциональные компоненты стали очень известны после появления хуков в React v16.8.

Методы жизненного цикла в функциональных компонентах просты в использовании благодаря хукам.

Компоненты классов трудно понять новичкам, которые не имеют опыта работы с ES6 JavaScript, где были представлены классы.

В отличие от других языков программирования, таких как Java, PHP и C#, классы JavaScript — это синтаксический сахар, а не наследование прототипов. Другими словами, классы ES6 — это просто специальные функции. Поэтому мы не можем использовать функции класса в классах JavaScript.

Компоненты класса играют важную роль в обработке ошибок. Поскольку компонент класса поддерживает только методы жизненного цикла границ ошибок static getDerivedStateFromError() или componentDidCatch().

Нет ничего лучше, потому что у обоих есть плюсы и минусы. Но компоненты класса важны для понимания потока React и методов жизненного цикла.

Новому учащемуся следует попрактиковаться в React, используя компоненты класса. Как только они познакомятся с компонентами класса, они смогут изучать и использовать функциональные компоненты.

Спасибо за чтение! Не стесняйтесь комментировать свои отзывы и делиться недостающими различиями между компонентами класса и функциональными компонентами.

Ссылки

https://reactjs.org/docs/react-component.html
https://reactjs.org/docs/components-and-props.html
https://reactjs.org/docs/state-and-lifecycle.html
https://reactjs.org/docs/higher-order-components.html
https://www.twilio.com/blog/react-choose-functional-components
https://blog.logrocket.com/handling-javascript-errors-react-error-boundaries/

Дальнейшее чтение



Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord . Заинтересованы в хакинге роста? Ознакомьтесь с разделом Схема.