Как стать автором
Обновить

Ненормальный React: давайте совместим классовые и функциональные компоненты в одно

Время на прочтение3 мин
Количество просмотров5.4K

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

Давайте посмотрим в чем преимущества и недостатки каждого из вида компонентов:

Классовые:

  • + Есть наследование

  • + Удобно расположить разные части кода по разным методам

  • - State может быть только один

  • - Везде надо писать this

  • - В целом больше кода

Функциональные:

  • + Короче

  • + Можно делать много state-ов

  • + useEffect с зависимостями выглядит логичнее всяких componentDidUpdate

  • - Весь код свален в одну функцию

  • - Вместо наследования не очень наглядные решения вроде пользовательских хуков

А есть все совместить? Я подумал и написал react‑fcomponents. Сразу скажу, что проект на ранней стадии и я даже не уверен, что буду им пользоваться сам, хотя возможно и буду. Но в этой статье я в любом случае объясню как им пользоваться.

Что такое react‑fcomponents? Если кратко, это класс, внутри которого работают хуки и есть несколько дополнительных удобств. Сам класс на данный момент очень короткий.

Давайте посмотрим как им пользоваться на следующем примере:

//ExampleClass.js

import { useEffect, useState } from "react";
import FComponent, { make } from "react-fcomponents";

class ExampleClass extends FComponent {
    useTest = (...args) => {
        useEffect(() => {
            console.log('mounted');
        }, []);
        return useState(...args);
    }

    useHooks() {
        this.useTest();
        this.useState('test', 0);
    }

    render() {
        return <div>{this.props.text}<button onClick={
            () => {
                this.setStates.test(this.states.test + 1)
            }
        }>{this.states.test}</button></div>;
    }

}

export default make(ExampleClass);

//App.js

import ExampleClass from './ExampleClass';

function App() {
  return (
    <div>
      <ExampleClass text="Hello World" />
      <ExampleClass text="Hello World2" />
    </div>
  );
}

export default App;

Начнем с конца. Как можно заметить, экспортируется не ExampleClass, а make(ExampleClass). Это делается для того, чтобы React не решил что это классовый компонент. Внутри make создает новый экземпляр класса, и возвращает хитрую функцию, которая в конечном счете запускает метод render. Но React считает компонент функциональным!

Теперь посмотрим на метод render:

render() {
        return <div>{this.props.text}<button onClick={
            () => {
                this.setStates.test(this.states.test + 1)
            }
        }>{this.states.test}</button></div>;
    }

С чего тут можно начать. Во‑первых, this.props работает. Кстати, это не обязательно — можно написать render (props) и далее работать без this. Но вот если вы будете работать с this в других методах класса, это пригодится.

Далее, в данном примере я не добавил в render хуки (они в методе useHooks, про который я расскажу позже), но их там можно спокойно использовать и они там будут работать. Вообще, render это содержимое функционального компонента и делать в нем можно все то же, что в функциональном компоненте.

Далее, мы видим весьма необычный метод работы со стейтами. Есть некие this.states.test и this.setStates.test. Очевидно, что это чтение и запись стейта, но откуда оно взялось? Для этого рассмотрим второй метод класса — useHooks:

useHooks() {
        this.useTest();
        this.useState('test', 0);
    }

useHooks — это опциональный метод класса, код в котором вызывается перед вызовом кода render. Предполагается, что в него вы положите вызовы хуков, чтобы не захламлять ими render. Здесь я вызываю два хука — this.useTest и this.useState. Первый рассматривать не вижу смысла — там просто какие‑то обычные useState и useEffect. А вот второй интереснее.

this.useState — это встроенный хук, который принимает на вход уникальное имя стейта и его значение по‑умолчанию. Как можно догадаться, внутри this.useState происходит вызов обычного React.useState, а далее его значение и сеттер кладутся в this.states[имя стейта] и this.setStates[значение стейта] соответственно. В итоге в любом методе класса появляется возможность чтения и записи стейта по его имени.

Что важно, этим тоже пользоваться не обязательно. Вы вполне можете вызывать обычный React.useState и класть его результаты в this. Просто использование this.useState может быть удобнее.

В общем, это пока все что я сделал. Результат, на мой взгляд, взял лучшее из обеих миров. Часто ли это придется использовать, пока не знаю. Но мне кажется, это мне пригодится.

Теги:
Хабы:
Всего голосов 4: ↑3 и ↓1+3
Комментарии12

Публикации

Истории

Работа

Ближайшие события

27 августа – 7 октября
Премия digital-кейсов «Проксима»
МоскваОнлайн
28 – 29 сентября
Конференция E-CODE
МоскваОнлайн
28 сентября – 5 октября
О! Хакатон
Онлайн
30 сентября – 1 октября
Конференция фронтенд-разработчиков FrontendConf 2024
МоскваОнлайн
3 – 18 октября
Kokoc Hackathon 2024
Онлайн
7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн