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

—

от автора

После того как 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 может быть удобнее.

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


ссылка на оригинал статьи https://habr.com/ru/articles/730766/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *