Иной взгляд на React компоненты

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

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Как мы все знаем, в React есть функциональные и классовые компоненты. Каждый вид имеет свои плюсы и минусы.

Классовые компоненты имеют меньшую производительность по сравнению с функциональными и вызывают некоторые сложности в переиспользовании одинаковой логики.

Моё мнение

Лично мне не нравятся повсеместные стрелочные функции и this.

Функциональные компоненты, в свою очередь, для оптимизации заставляют нас оборачивать объекты в useMemo, а функции в useCallback. Что уменьшает читаемость кода, а при большом количестве вызовов также понижает производительность (как бы это не было парадоксально).

Вы можете задаться вопросом: "Разве у нас есть иной вариант?". Да, он существует!
Что, если взять функциональный компонент и добавить ему функцию "конструктор", подобно одноимённому методу в классах?. Тогда нам не потребуется оборачивать в useMemo и useCallback, так как объекты и функции будут создаваться один раз. Также мы не потеряем удобное переиспользование логики и нам не потребуется this на каждой строке.

Довольно заманчивые условия, но разве это возможно сделать без "костылей"?
Я задался этим вопросом и нашёл решение: использовать замыкания для реализации "конструктора". После нескольких вечеров на просторах интернета "родился" npm-пакет react-afc.

Как мог выглядеть компонент со сложной логикой на чистом React:

import React, { useMemo, useState, useCallback, memo } from 'react'
import ComplexInput from './ComplexInput'
import ComplexOutput from './ComplexOutput'

function Component(props) {
  const [text, setText] = useState('')
  
  const config = useMemo(() => ({
    showDesc: true,
    title: 'Title'
  }), [])

  const onChangeText = useCallback(e => {
    setText(e.target.value)
  })

  const onBlur = useCallback(() => {
    // hard calculations
  })

  return <>
    <ComplexInput value={text} onChange={onChangeText} onBlur={onBlur} />
    <ComplexOutput config={config} />
  </>
}

export default memo(Component)

Пример абстрактный, но даже в нём уже видны проблемы частого оборачивания сущностей. С усложнением компонента становится только хуже.

Тот же пример, но с использованием react-afc:

import React from 'react'
import { afcMemo, useState } from 'react-afc'
import ComplexInput from './ComplexInput'
import ComplexOutput from './ComplexOutput'

function Component(props) {
  const [text, setText] = useState('')
  
  const config = {
    showDesc: true,
    title: 'Title'
  }

  function onChangeText(e) {
    setText(e.target.value)
  }

  function onBlur() {
    // hard calculations
  }

  return () => <>
    <ComplexInput value={text.val} onChange={onChangeValue} onBlur={onBlur} />
    <ComplexOutput config={config} />
  </>
}

export default afcMemo(Component)

Что же изменилось?

Теперь функция компонент является "конструктором" и вызывается только один раз в течение всего жизненного цикла компонента. Это значит, что onChangeText, onBlur и config одинаковые каждый рендер (без использования обёрток), то есть они не вызывают перерисовку "детей" при обновлении компонента. Конструктор возвращает рендер-функцию, которая вызывается каждый рендер.

Что насчёт производительности?

Пакет максимально переиспользует React-хуки: при нескольких вызовах useState из react-afc используется лишь один хук из React. Это ломает просмотр состояний компонентов в ReactDevtools, но такова цена производительности.

В целом, разница в производительности незначительна. Но чем сложнее компонент, тем выше разрыв между обычными и afc-компонентами (react-afc может быть до 10% быстрее).

Пакет может измениться в будущем. Переписывать на него существующие проекты не нужно. А вот использовать в новых может быть очень даже удобно.

Жду вашего мнения в комментариях :)

Источник: https://habr.com/ru/post/711940/


Интересные статьи

Интересные статьи

Недавно меня спросили: “Зачем я отказался от библиотеки react-router и перешел на свой велосипед?” Честно говоря, вопросы, связанные с моим вариантом роутинга, мне задавали уже раз пять. Последний раз...
Всем привет. Меня зовут Дмитрий Андриянов. Два года писал на React Native, сейчас я разработчик в Surf и уже полтора года пишу на Flutter. Когда я только решил серьёзно взяться за Flu...
Всем привет. Когда я искал информацию о журналировании (аудите событий) в Bitrix, на Хабре не было ни чего, в остальном рунете кое что было, но кто же там найдёт? Для пополнения базы знаний...
Состояние используется для организации наблюдения за данными React-приложений. Состояния меняются по мере того, как пользователи взаимодействуют с приложениями. Когда пользователь выполняет некое...
Тема статьи навеяна результатами наблюдений за методикой создания шаблонов различными разработчиками, чьи проекты попадали мне на поддержку. Порой разобраться в, казалось бы, такой простой сущности ка...