Pull to refresh

Comments 133

Спасибо за статью!


Пара вопросов:


  1. (react/no-direct-mutation-state)
    updateWithImmutabelJS = () => {
    const newState = this.state.set('iUseImmutableStructure', true);
    this.setState(newState);
    }

Раньше вроде нельзя было использовать immutable-js как стейт, теперь можно?


  1. (react/jsx-no-bind)
    Очень распространенная и большая ошибка которую я видел в коде много раз. Не Используйте Bind И Arrow Function В Jsx. Никогда. Больше.
    Самое жаркое место в аду ждет того кто пишет .bind(this) в JSX.

Вот это очень обидно щас было.


Во-первых https://reactjs.org/docs/handling-events.html, Ctrl+F: bind(this
Во-вторых такие функции не остаются в прототипе
В-третьих подробнее https://medium.com/@charpeni/arrow-functions-in-class-properties-might-not-be-as-great-as-we-think-3b3551c440b1


Я как бы только за не бойлерплейтить .bind, но хотелось бы обоснований =(

"Нативный" .bind при каждом вызове возвращает новую функцию, такова его природа. То же и со стрелочными функциями. Каждый новый вызов функций, в которые транспилируется jsx, приводит к новой функции в обоих случаях, что означает новую ссылку. Т.е. для пользовательских компонентов (с большой буквы) мы получаем как минимум лишнюю реконсиляцию. А для пользовательских и для встроенных компонентов (с маленькой буквы) — гарантированное создание новой функции при каждом рендере. Что приводит либо к повышению нагрузке при GC, либо к утечкам памяти.

Эээ, .bind обычно делают в конструкторе ```this.handleClick = this.handleClick.bind(this)```

Ну и я к тому, что заявлять «должны гореть в аду» можно за «with {}», «goto», «jQuery» (:trollface:), но вот про .bind vs class properties это чересчур.
UFO just landed and posted this here
Спасибо. Я имел в виду именно этот вызов bind. Хотя в других местах я его тоже не очень люблю. По большей части из-за проблемы описанной dagen чуть выше.
Интересный момент. Раньше и не задумывался как-то, хотя не думаю, что это сильно скажется на скорости или памяти в обычных приложениях.

А как в этом случае передавать параметры?

<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
UFO just landed and posted this here
Akuma TL;DR: мало кнопок — оставить так, много кнопок — обернуть в функциональные компоненты.

Конкретно в этом случае (с button) не будет перерисовок (Реакт вынужден вызывать лишние реконсиляции только для пользовательских компонентов). Поэтому если у вас не так уж и много строк с кнопками, то самым выгодным вариантом будет так и оставить. Если много — то как ниже советует faiwer — обернуть в компоненты.

Только надо учесть, что выгода от компонентов будет только если используются stateless functional компоненты, и при этом нет observer от mobx-react (и других подобных вещей, напр. из recompose). Декоратор observer превращает ваши функциональные компоненты в обычные class-based, что означает, что вы променяете кучу функций, создающихся на каждый рендер, на кучу инстансов компонентов, по одному для каждой кнопки. А раз мы рассматриваем это только при варианте, когда кнопок много, то это будет расточительно.
Конкретно в этом случае (с button) не будет перерисовок

Будет removeEventListener и addEventListener. Просто потому что react понятия не имеет о том, что это по факту один и тот же метод. reflow из-за этого не будет, да, это просто eventListener. Согласен, что по факту это ерунда и такие вещи в браузерах должны быть очень заоптимизированы. И тут, наверное, лучше писать как удобнее и проще работать с кодовой базой, нежели считать такты ЦП.

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


UPD. Проверил в CodeSanbox, так и есть

А кто-нибудь вымерял улучшение производительности?
Мне кажется это как echo «string»; и echo 'string'; в PHP — экономия на спичках.

Тут всё сугубо зависит от вложенности. Если речь об единичном теге <button/> то тут речь скорее про общий порядок и приверженность одному подходу вдоль всего приложения. А если речь идёт о чём-то большом, то там и тормоза будут уже ощутимые визуально. Вот вам вырожденный пример.


  • Имеем некий <MyTable/> в котором 200 <MyTableRow/> и в них по 200 <MyTableCell/>
  • Пишем всё это ногами, без PureComputed, без bind и пр.
  • В <MyTable/> передаём onClick={this.onClick.bind(this, 42)}
  • Получаем, что при каждом рендере компонента, в котором есть <MyTable/> мы заставляем React рендерить вообще всю таблицу всегда

На самом деле ситуация надуманна, т.к. если программист будет писать в таком стиле, он убьётся о тормоза ещё в самом начале и начнёт думать головой. И да, будут у него и bind-ы и PureComputed-ы и прочие ужасырадости аля functional way.


Так что всё зависит от конкретных задач. Чем крупнее и сложнее проект, тем раньше эти вещи станут очевидными. И да, с тормозами можно и не столкнуться, пока проект это просто набор простых формочек.


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

Ну… да, пример конечно очень надуманный :)
Как представится возможность, хочу все же на реальном приложении протестировать улучшения. Если вдруг не забуду, отпишусь.
А то такие примеры из разряда «я залез на Эверест, прострелил себе колено и не могу слезть. Виноват React».
я залез на Эверест, прострелил себе колено и не могу слезть. Виноват React

Беда в том что правда лезут и простреливают. Массово. Тут коллега жаловался недавно, что у него инициализация проекта (куда его пригласили) 45 секунд в браузере занимает. На каждый чих по 3-4 HoC-а. Про reselect никто никогда не слышал. Про React way видимо тоже. Наверное "преждевременная оптимизация корень всех зол" было их лозунгом, а потом legacy накопилось и стало "тут уже ничего не исправить, Господь — жги".

У меня некоторое время довольно-таки хорошо работала такая конструкция (пишу без предварительного тестирования — по памяти, так что код может быть нерабочий, но, главное, из него будет понятен смысл):

class MyRows extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            rows: props.rows,
        };
    }

    /**
     * @param {SyntheticEvent} event
     */
    handleDelete = event => {
        let rows = Object.assign({}, this.state.rows);

        delete rows[event.target.dataset.rowId];

        this.setState({rows});
    }

    render() {
        return <div>
            {Object.values(this.state.rows).map(row => {
                return <button
                    key={row.id}
                    onClick={this.handleDelete}
                    data-row-id={row.id}
                >Delete row #{row.id}</button>;
            })}
        </div>;
    }
}


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

Нет, конечно. В обычном компоненте вы можете создать функцию внутри него и вызывать ее this.functionName. В случае stateless компонента функцию можно вынести наружу, иначе она будет создаваться каждый раз заново при перерендере компонента.

Эээ, .bind обычно делают в конструкторе ```this.handleClick = this.handleClick.bind(this)```

Конечно если делать бинд, то в конструкторе (а лучше вообще не делать так, чтобы нужно было использовать бинд для обработчиков). Я отвечал на ваш вопрос, в котором вы процитировали автора:
Не Используйте Bind И Arrow Function В Jsx. Никогда. Больше.
Запрет на использование в свойствах биндинга или стрелочных функциях выглядит как преждевременная оптимизация.

https://github.com/facebook/immutable-js/wiki/Immutable-as-React-state
По поводу state вы правы. Здесь будет верно:


const newState = this.state.data.set('iUseImmutableStructure', true);

К сожалению, документация по Реакту уже довольно давно не поспевает, так сказать, за прогрессом и некоторые моменты там немного устарели. Про bind чуть ниже написано пару хороших комментариев. Рекомендую почитать. По поводу прототипирования в реакте первый раз услышал от вас. Используйте композицию вместо наследования (вольный перевод высказывания Gang of Four).
Не нашел в статье под пунктом три ничего критического для себя, что бы заставило меня не использовать стрелочные функции в компонентах. Ключевое слово static безусловно полезно. Я обычно использую его для propTypes внутри компонента, как и описывал автор.

В-третьих подробнее https://medium.com/@charpeni/arrow-functions-in-class-properties-might-not-be-as-great-as-we-think-3b3551c440b1

белки-истерички.jpg. Других ассоциаций поле прочтения этой статьи у меня нет. Да, => не серебряная пуля. Своего рода костыль. Но реально столкнуться с такой проблемой отсутствия метода в прототипе можно только начав что-то наследовать (довольно редкое явление в React-экосистеме). Там, если захочется сахара, нас могут выручить те же декораторы.


Всё что там написано про производительность, имхо, вообще та ещё муть. Даже на довольно больших проектах она незаметна. На фоне всех аллокаций наши лишние => просто теряются.


Мне кажется в большинстве случаев (99.9+%) использование => для решения проблемы контекста более, чем оправдано.

(react/no-did-mount-set-state)

Официальная документация считает иначе. В componentDidMount можно вызывать setState:


You may call setState() immediately in componentDidMount().

Про componentDidUpdate верно, можно устроить бесконечный цикл, но did mount тут не при чем

все так. здесь слово «may» это «можно, если без этого ну вообще ни как». по идее, react/no-did-mount-set-state ловит лишь те ситуации, когда setState вызывается синхронно из componentDidMount. здесь синхронный вызов хоть и не вгоняет компонент в бесконечный цикл, но все равно приводит к повторному вызову render (о чем документация тоже предупреждает). асинхронно вызывать ни кто не запрещает.

airbnb недавно выключили проверку из-за server-side rendering: github.com/airbnb/javascript/issues/684#issuecomment-355625957

Спасибо за дополнение. При изменении стейта componentDidMount можно вызвать перерендер компонента еще один раз. Если вам нужно поменять стейт после перерендера, возможно, стоит вынести эту переменную как переменную класса:


class MyComponent extends React.Component {
  state = {
    varThatAffectRender: true,
  }

  varThatNotAffectRender = true;
}

А как быть с таким примером:


componentDidMount() {
   this.loadData();
   this.setState({loading: true});
}

render() {
  if(this.state.loading) {
     return <Loader />
  }
  //...
}

Здесь переменная класса не сработает, нужно лоадер показать.

В данном случае вы можете выставить loading в true в state, или перенести его в пропсы и выставить static defaultProps = { loading: true };
> В данном случае вы можете выставить loading в true в state

То есть, вы имеете в виду все-таки вызвать setState?

Нет. Я имею ввиду:


class MyComponent extends React.Component {
  state = {
    loading: true,
  }

  render() {
    const { loading } = this.state;
    if(loading) {
       return <Loader />;
    }
    //....
}
а если загрузка в componentDidMount происходит не всегда, а по какому-то условию? Придется это условие дублировать в инициализаторе.

А все ради сомнительной экономии на спичках.

Вы просто это условие переносите в инициализацию стейта или конструктор если вам нужны пропсы.
Дублировать как раз ничего и не нужно будет в этом случае.
Если компонент очень тяжелый (например, таблица с 5к строками) и рендериться очень много времени, вы сможете сэкономить 50% времени от начального рендеринга, а это может быть очень не мало.

Откуда цифра в 50%? В документации про componentDidMount сказано, что в браузер выведется только результат второго рендера, после вызова setState. Максимум, что вызовется лишний раз, это метод render, что не так уж много.


Я тут провел свое исследование, отрендерил таблицу на 5к строк (вот код):



Какой-то особой разницы для пользователя не видно, что там, что там страница готова примерно через 1,5 секунды.


Если и искать способы ускорить свое React-приложение, то точно не в этом setState.

Поменяйте в setState массив строк который вы передаете в рендер компонента и увидете разницу.
Вроде изначально разговор шел о том, чтобы поменять значение флага `loading`, а не набор элементов целиком…

Конечно, если постараться, можно заставить компонент тормозить, но от небольших изменений стейта ничего не испортится.
Проблема про которую я говорю в статье описывает ситуацию изменения стейта в целом. Не важно насколько они значительные или нет. Чем они больше тем тормознее будет ваш компонент. Но негативный эффект все равно будет. Не важно, что при изменении всего одной переменной он не значительный, он есть и это факт. При изменении массива в 5к объектов, который вы положили в стейт, вы получите еще больший негативный эффект.
Основная беда подобных статей то, что там пишется «не используйте X, он медленный» без объяснения при каких условиях и почему.
В результате неопытные пользователи начинают заниматься такими «оптимизациями», ухудшая читаемость кода и порождая баги.

Как написали ниже другие комментаторы — преждевременная оптимизация — не ок. Советы типа react/no-did-mount-set-state больше похожи на карго-культ, реальная оптимизация делается по-другому
сначала вызывается render, а потом componentDidMount. чтобы ваш пример сработал как ожидается при первой отрисовке компонента, state.loading должен быть проинициализирован true где-то раньше (как вариант — в конструкторе). поэтому в целом такая конструкция с setState только за зря дергает render еще раз.
Это один из тех случаев, где можно перемудрить с преждевременной оптимизацией. Если какая-то задача решается в один вызов setState, то лучше так и сделать, чем изобретать альтернативное решение (и не факт, что более производительное).
(react/forbid-prop-types)
Описывайте ваши props как можно подробнее.

Если уж у вас есть желание описать свойства подробнее, то лучше заморочиться с TypeScript/Flow. С ними и переиспользуемость кода лучше, и в текстовых редакторах лучше поддержка.

TypeScript делает статический анализ на этапе компиляции, а PropTypes — на этапе выполнения. тут нельзя однозначно сказать, что лучше. это очень старая история.
Последний раз когда писал на react + flow, то в IDE phpstrom не работал автокомплит, приходилось дублировать через propTypes

Потому что flow, как ни странно, не про автокомплит, а про проверку корректности программы на основе информации о типах.
В TypeScript с этим веселее, и корректность на лету проверяется (хотя в теории система типов у flow несколько строже), и автокомплит весьма приятный, и web/phpstorm с ним лучше дружат.

Я понимаю, что такое flow. В комментарии выше было про «переиспользуемость кода лучше, и в текстовых редакторах лучше поддержка», вот я и написал про автокомплит.
К тому же flow для статического анализа типов, а propstypes делает это рантайм. Так что лучше использовать оба инструмента.

p.s. А как давно typescript рантайм проверяет типы? Раньше он так делать не умел и приходилось использовать дополнительный пакет.

TypeScript и не проверяет в рантайме. Но, рассуждая логически, откуда в рантайме может прилететь неверный тип? Либо мы где-то поленились и написали any — ССЗБ. Либо из внешних источников пришло что-то неожиданное (с сервера или там из localStorage). Тут propTypes поможет, но лучше было бы осуществлять такую проверку там где эти данные пришли, а не на стадии отображения.

Typeguard-ы компилируются в JS только пользовательские. И они не кидают исключения — точнее, конечно, будет исключение, если мы попытается получить свойство у null или вызвать undefined, но нам это ничего нового не скажет.

Поддержка flow в том же vscode просто ужасная (отдельный плагин).
Постоянно виснет, плодит процессы flow.exe. Автокомплит от flow работает через раз, а родной приходится отключать.
Может конечно у фэйсбука есть какая-то своя IDE?

Ну, значит, пользуйтесь TypeScript. Я с ним работаю и в WebStorm и в VS Code, все нормально.


Flow я просто упомянул как еще одну альтернативу.

(react/prefer-stateless-function)

Данное правило описывает больше улучшение вашего кода и приложения, чем ошибку, но я, все же, рекомендую следовать этому правилу. Если ваш компонент не использует state, сделайте его stateless компонентом (мне больше нравиться термин 'pure component'):

class MyComponentWithoutState extends React.Component {
    render() {
        return <div>I like to write a lot of unneeded code</div>
    }
}

const MyPureComponent = (props) => <div>Less code === less support</div>



А ничего что stateless и pure components это совсем разные понятия?
И что функциональные компоненты не являются чистыми?
И использование их без тулов типа recompose просадит производительность?

Поискал сейчас специально, но к сожалению, не нашел официального определения stateless компонентов и pure components. Если попытаться перевести дословно, то stateless значит без стейта, без состояния, что в целом верно описывает компонент. pure component же можно попробовать перевести как чистый компонент, что подразумевает отсылку к чистым функциям из функционального программирования.
Что же такое чистая функция? Wiki
Вики говорит, что фунция чистая если:


  1. является детерминированной;
  2. не обладает побочными эффектами;

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

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

Это ж в какой вселенной наличие слова чистый подразумевает отсылку к чистым функциям?
Если у меня есть чистая тарелка то что она должна быть детерминирована?

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

Суммируя мы можем назвать этот компонент чистой функцией.

Этот компонент будет pure function и stateless component, но не pure component.
И в библиотеке recompose есть HOC который называется pure и делает компонент pure, что как бы наталкивает на мысль, что обычный функциональный компонент не является pure.
Это ж в какой вселенной наличие слова чистый подразумевает отсылку к чистым функциям?
Если у меня есть чистая тарелка то что она должна быть детерминирована?

Простите, но не совсем понял при чем здесь чистая тарелка. Я пытаюсь объяснить, что именно я подразумеваю под понятием PureComponent. Это лично мое определение.
И в библиотеке recompose есть HOC который называется pure и делает компонент pure, что как бы наталкивает на мысль, что обычный функциональный компонент не является pure.

Я не использовал библиотеку recompose в продакшене, но читал пару статей про нее. Не понимаю, при чем здесь данная библиотека и мое определение чистого компонента.
Простите, но не совсем понял при чем здесь чистая тарелка.

А я не совсем понимаю при чём здесь чистая функция?
что подразумевает отсылку к чистым функциям из функционального программирования.

Каким образом?

Компонент чистый, когда при одних и тех же пропсах не вызывается рендер, и соответственно не происходит просчёт всех компонентов которые находятся ниже.

А можно по подробнее почему не стоит использовать stateless components без recompose? И почему они просаживают производительность?

import React from 'react';

// любой список или таблица с большим кол-вом данных 10к например
const Table = ({ data }) => (
    <table>
        {data.map(() => {
            // tr td what ever
        })}
    </table>
);

const Wrapper = ({ searchString, onSearchStringChange, tableData }) => (
    <div>
        <input value={searchString} onChange={onSearchStringChange} />

        {/* любое изменение в input будет вызывать пересчёт Table */}
        <Table data={tableData} />
    </div>
);

И выйдет так что пользователь вводит слово в input, а он лагает потому что js поток пытается выполнить data.map() и занимает это дочерта.
А если Table чистый компонент, то он не будет даже data.map выполнять потому что массив data не изменился.

А если у нас внутри Table ещё что-то тяжёлое, ещё больше нагрузка и больше лагов.
При таком раскладе само собой. Но все надо делать с умом) из Вашего комментария, я так понял что Вы имеете в виду вообще отказаться от их использования.
Мне проще всё писать в одном стиле и сразу чистым, и если уж возникает необходимость убирать pure. Основная мысль — дефолтно всё pure.
Если бы это работало лучше, каждый компонент в реакте был бы pure по дефолту. Достаточно часто операция поверхностного сравнения для тупого компонента будет дороже чем выполнение рендера самого компонента, особенно если мы говорим о мелких атомарных компонентах, которые отвечают за рендер небольшого участка дома. Поэтому, но мой взгляд это не совсем верно. Это как прописывать shouldComponentUpdate в каждом компоненте (до появления pureConponent), вряд ли Вы так делали.
каждый компонент в реакте был бы pure по дефолту

Я полагаю, что они давно кусают локти, что не сделали так с самого начала. А теперь уже поздно.


Достаточно часто операция поверхностного сравнения для тупого компонента будет дороже чем выполнение рендера самого компонента

Как вы себе это представляете? Нет серьёзно. Вот есть у нас типовой объект props с 5 полями. Итого у нас 6 ===. И вот есть у нас типовой рендер с 6-8 тегами. Это аллокация целой кучи объектов, а затем ещё реконсиляция. И всё впустую. Мне кажется эти вещи по времени выполнения вообще не сопоставимы.


Я вижу только 1 случай, когда 2-е может перебить 1-е. Это когда наш компонент в 80+% случаев и правда требует re-render-а, т.к. там что-то меняется. Т.е. чуть ли не каждый первый render в нём приведёт к реальным DOM-операциям. И тогда и правда у нас проверка почти бесполезна. Но даже в этом случае на общем фоне выполняемых задач она почти невесомая.

Честно говоря чтоб сказать однозначно — надо потестить, мне как-то никогда в голову не приходило (либо не было надобности) использовать PureComponent на каждом компоненте, если есть эта проверка на тех компонентах, которые, допустим, рендерят большие списки, то проверять еще каждый элемент этого списка — помоему лишняя операция сравнения в процессе апдейта. В случае если вы на 80+% уверены что компонент не будет ререндериться, тогда уж проще просто вернуть false из shouldComponentUpdate. К тому же не забывайте о том что при использовании PureComponent, если компонент все-таки будет обновляться — будут вызываться все lifecycle hooks компонента, которые Вам (возможно) в данном случае не нужны.

Вот нашел твит Абрамова на этот счет
twitter.com/dan_abramov/status/759383530120110080
В случае если вы на 80+% уверены что компонент не будет ререндериться, тогда уж проще просто вернуть false из shouldComponentUpdate

Не понял, что вы этим хотели сказать? .forceUpdate вызывать? оО


К тому же не забывайте о том что при использовании PureComponent, если компонент все-таки будет обновляться — будут вызываться все lifecycle hooks компонента

fix Хм. Не сразу правильно прочёл. Поясните, плз, что вы этим хотели сказать. Тут что PureComponent, что просто Component, одинаковое поведение. Не понял мысль.


Касательно твита. Не нашёл там ничего умнее, чем:


Basically compare + render anyway is slower than just render anyway

Какое откровение?! :) Я комментом выше об этом и написал. Впрочем от него я и не ожидал, чего-то умного. Что вы с ним носитесь, как с писанной торбой (не именно вы, а вообще сообщество).

Кажется я понял. Вы имеете ввиду, что если использовать вместо PureComponent не Component, а просто функцию (ака stateless notation)? А разве это не упрощённая запись Component? Разве там предусмотрена для них какая-то другая логика lifecycle? Я в код не лез, но думаю, что алгоритм един, это лишь вопрос сахара.

Кажется я понял. Вы имеете ввиду, что если использовать вместо PureComponent не Component, а просто функцию (ака stateless notation)?

Верно, комментарий изначально вроде был именно о них.

А разве это не упрощённая запись Component?

На сколько я знаю, это одна из «фичей» функциональных компонентов, что они опускают всякую всячину вроде хуков. По крайней мере сравнение транспиленного объявления этих компонентов говорит о наличии какой-то разницы)
var A = function (_React$Component) {
   _inherits(A, _React$Component);

   function A() {
      _classCallCheck(this, A);

      return _possibleConstructorReturn(this, (A.__proto__ || Object.getPrototypeOf(A)).apply(this, arguments));
   }

   _createClass(A, [{
      key: "render",
      value: function render() {
         return React.createElement("div", null);
      }
   }]);

   return A;
}(React.Component);

var B = function B() {
   return React.createElement("div", null);
};

В трансплайенном коде вы ничего и не увидите (он вообще ни о каком React ничего не знает же). Тут надо смотреть что происходит в недрах обработки VDom древа. Я думаю, что там поведение идентичное обычному пустому class-Component-у. Очень сомневаюсь что там будет какая-нибудь статистически непренебрежимая разница. Это уже даже не экономия на списках, а на атомах в этой спичке. Сродне ++ i >> i ++ :)


Впрочем, было бы любопытно послушать тех, кто такие замеры проводил или ковырялся с fiber-исходниках.

Профит от как минимум меньшего количества кода, вроде как тоже не последнее дело в общем итоге.
Практически все статьи, что мне встречались по этому поводу советуют все-таки «разумно» использовать PureComponent. В любом случае ради интереса было бы интересно увидеть на практике в сравнении рендер двух больших списков в разных вариациях.
Статья для 14 реакта, и там видна разница между Component и functional Component она 6-7%

Я взял код оттуда и зделал 16 реакт, тесты показали тот же прирост всего в 6-7%

А вот простой вызов функционального компонента как функции даёт прирост в 60%, что наталкивает на мысль, что там далеко не простой вызов функции, и логики там даже на функциональные компоненты накидывается дочерта.

И никто не мешает мемоизировать функциональный компонент))

6-7% не так уж и мало, на самом деле, я думал будет в р-не 1-2%. Спасибо за ссылку. Почитаю.

Хм:


There is no “optimized” support for them yet because stateless component is wrapped in a class internally. It's same code path.

как я и думал, но


This pattern is designed to encourage the creation of these simple components that should comprise large portions of your apps. In the future, we’ll also be able to make performance optimizations specific to these components by avoiding unnecessary checks and memory allocations.

Парни решили этого не ждать, и просто забить на JSX. Все функциональные компоненты сразу вызывать как MyComponent(innerProps) и получили 45% производительности, избежав всей React машинерии.


Т.е. потенциально тут может быть существенный прирост в будущем (45%).


Отдельный правда вопрос — а дала ли им эта экономия на спичках какой-либо существенный результат всецело (с учётом всех факторов, таких как DOM)? Но такое померить сложнее.


Ну и отдельный момент, в сравнении с PureComputed. Тут можно отыграть (на shouldComponentUpdate) куда больше этих 45%. Как минимум расставив их в тех местах, где VDom-а много (уж очень недёшего его вглубь проверять, там ведь не shallow).

Я пытаюсь объяснить, что именно я подразумеваю под понятием PureComponent. Это лично мое определение.

Однако в React есть вполне конкретный PureComponent (который совершенно не обязательно чистый, зря они такое название выбрали). Лучше не путать себя и других :)


Тут не так давно у на уже был вселенский срач на тему React и функциональное программирование. Вроде как решили, что React довольно далёк от настоящей функциональщины.

разница между Functional Component и React.PureComponent хорошо описана в stackoverflow.com/a/40704083/3297887

tldr;
* Functional Component и React.Component с единственным методом render это одно и то же. но первое требует меньше писанины. поэтому eslint рекомендует этот вариант.
* React.PureComponent это компонент с оптимизированным shouldComponentUpdate (чего нет у первых двух). добавить фичу наследникам React.Component можно реализовав метод лапками. Functional Component — обернуть в HOC с такой функциональностью (можно взять готовый из recompose).
В целом всё верно, но есть пара спорных пунктов.

Эта ошибка так же очень часто встречается в моей практике. Если вы попытаетесь обновить state в componentDidUpdate или componentDidMount методах, вы получите бесконечный цикл ре-рендера.

Это не ошибка. Не редко без этого вообще не обойтись, когда у тебя есть компонент со своеобразным поведением и зависящий от внешнего состояния. Тут нужно быть осторожным и не обновлять то состояние, которое вызовет бесконечный цикл. Другими словами обновлять состояние тут можно, но по необходимости.

Мое персональное мнение это использование immutable-js, как библиотеку, которая добавляет иммутабельные структуры.

Моё персональное мнение — не использовать эту библиотеку. Она очень тормозная, с большими данными работать невозможно. В своё время обжёгся на этом. Да и с тайпингами геморрой страшный. Использую spread оператор и всем советую.

Про componentDidMount ответил выше. Если в кратце, то вы вызовите еще одни ре-рендер. И я предлогаю использовать для вещей которые нужно менять в componentDidMount переменные класса.
ImmutableJs работает быстрее с данными, чем аналогичные структуры из JS (Map, List). Буквально недавно делал бенчмарки по map функции. Если интересно, можете поискать результаты.

Если вы попытаетесь обновить state в componentDidUpdate или componentDidMount методах, вы получите бесконечный цикл ре-рендера
Интересно как можно получить бесконечный цикл ре-рендера при setState внутри componentDidMount если этот метод срабатывает только один сразу после маунтинга компонента и в дальнейшем никак не реагирует на любые изменения стэйта и пропсов?

По поводу componentDidUpdate то это место где как раз рекомендуется делать апдейт стейта, в случае если например для этого надо сделать какой либо запрос на сервер в зависимости от определенного состояния пропсов, главное правило которое должно здесь соблюдаться это «it must be wrapped in a condition»

Про componentDidMount ответил выше. Если в кратце, то вы вызовите еще одни ре-рендер. И я предлогаю использовать для вещей которые нужно менять в componentDidMount переменные класса.

Речь только про синхронный setState. В том смысле, что если у вас в didMount/didUpdate идет сразу вызов setState, сначит вы могли бы перенести эту логику в конструктор/место вызова первичного setState, т. к. ничего еще не поменялось, и сэкономить рендер. В колбеках ajax-запросов и т.п. само собой можно использовать setState в обоих методах.
Если вы попытаетесь обновить state в [...] componentDidMount методах, вы получите бесконечный цикл ре-рендера.

Чего-чего? Я что, пропустил какой-то свежий апдейт Реакта?
Давайте вместе проверим:
reactjs.org/docs/react-component.html#componentdidmount

You may call setState() immediately in componentDidMount(). It will trigger an extra rendering, but it will happen before the browser updates the screen. This guarantees that even though the render() will be called twice in this case, the user won’t see the intermediate state. Use this pattern with caution because it often causes performance issues. In most cases, you should be able to assign the initial state in the constructor() instead. It can, however, be necessary for cases like modals and tooltips when you need to measure a DOM node before rendering something that depends on its size or position.

«Use with caution», но «it can be necessary».
Не вводите людей в заблуждение почём зря.

Про componentDidMount ответил выше. Если в кратце, то вы вызовите еще одни ре-рендер. И я предлогаю использовать для вещей которые нужно менять в componentDidMount переменные класса.

И я предлогаю использовать для вещей которые нужно менять в componentDidMount переменные класса

Тут всё просто. Всё, что не статично и при этом используется в .render должно быть либо в props либо в state. Всё остальное можно хранить где и как угодно. Потому что на изменения .props и .state react умеет реагировать, на ваши обвязки — нет.


И если ваше значение, которое вы ставите в componentDidMount завязано на render, то стало быть второму render-у быть. Это нормально. Вообще несколько вызовов render в сложных компонентах явление частое. Скажем зачастую приходится вызывать .setState при получении реальной ссылки на DomElement (к примеру, чтобы узнать размеры объекта).

> Скажем зачастую приходится вызывать .setState при получении реальной ссылки на DomElement (к примеру, чтобы узнать размеры объекта).
То что вы описали отлично сохраняется как переменная класса. Я как раз и использую ее частенько для сохранения ссылок на элементы в доме. Если можно избежать второго рендера почему этого не сделать? Преждевременная оптимизация? По-моему просто попытка написать нормальный и производительный код. К сожалению, последнее время фронт-енд разработчики перестали заботиться о производительности и размере бандла заранее. Нужно вначале довести до того что бы предложение безбожно тормозило и размер бандла был за 10 Мб, и только после этого заниматься оптимизацией. Мой вопрос заключается в том, что почему если вы знаете проблему, как опытны разработчик, не исправить ее сразу не доводя до крайности? Еще раз. Это не преждевременная оптимизация, это просто попытка написать чистый и производительный код.
Если можно избежать второго рендера почему этого не сделать?

А как? Вот взять мой недавний случай. Мне надо иметь точные значение offsetWidth и offsetHeight чтобы просчитать нужные коэффициенты для работы с <svg/>. Пока вы DOMElement не получите, вы не узнаете их. А узнав — нужно делать re-render.


Какой именно use-case вы собираетесь покрыть своим решением? Приведите пожалуйста реальный пример. А то совсем непонятно. Быть может там никакой componentDidMount и не нужен вовсе.

В вашем примере вам нужно получить ссылку на уже отрендеренный компонент и получить его размеры. Вы сохраняете ссылку на объект в переменной класса, так как эта информация не нужна вам для рендеринга. Далее в componentDidMount вы получаете значения и вызываете ре-рендеринг компонента. В этом случае обходного пути я не вижу, но я пытаюсь донести именно проблему данного подхода. Зачем вам рендерить именно весь компонент? Возможно, можно создать его скрытое зеркало, получить размеры и потом отрендерить сам компонент уже со всем содержимым. В моей практике был всего один раз когда мне понадобилось делать данную бяку: это связано с textarea и текстом в нем. Что бы узнать нужный мне размер textarea для его последующего рендеринга, я отрисовывал его скрытое зеркало в доме, брал размеры, а потом рендерил textarea с текстом. Связано это с тем, что размер текста может быть разным в зависимости от браузера, платформы и так далее. Есть вариант решения данной проблемы используя рендеринг текста в канвасе и считывания его размеров. Вы так же можете написать компонент который будет рендерить текст в канвасе и потом, обращаясь к нему, получать нужные вам размеры. Но описываемый мной пример, скорее, исключения из правила, подтверждающее верность данного правила. Вся реализация этого компонента выглядит как страшный костыль и пугает меня каждый раз когда я вижу его код. Я написал его не потому, что я так захотел, а потому, что порой во фронте случается такая боль.

Зачем вам рендерить именно весь компонент? Возможно, можно создать его скрытое зеркало, получить размеры и потом отрендерить сам компонент уже со всем содержимым.

Накладные расходы на "зеркало" превысят всю возможную мышиную возню на React. React работает с VDom. А зеркало это реальный DOM. Разница в производительности — на порядки.


Добавьте к этому то, что реальный размер для зеркала вы получите только, зная реальное место, куда его поместить. Для этого вам может потребоваться реальный DOMElement предка. А его вы до первого render-а так и так не получите. Ух. Приехали едва начав.


Ну либо захардкодив его ID-ом. Чудесно. В итоге даже несмотря на все ухищрения и порчу кодовой базы — мы проиграли по производительности. Т.к. нам потребовалось создать новый элемент, сделать reflow, удалить его, и потом снова reflow.


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

Самое время переписать его задействовав двойной рендер :) Поймите, операции с DOM, такие как reflow ОЧЕНЬ дорогие. На фоне их мышиная возня с React-ом (в вашем случае render VDOM для одного v-тега) это просто 0.


Я вам даже больше скажу. Сейчас в моде повсюду HOC матрёшками делать. Может легко и 4-5 десятков быть на сложных проектах (могу показать скриншот). А каждый (почти) HOC = отдельный компонент = отдельный react-render. И ничего, летает как истребитель. Потому что вся такая обвязка относительно легковесна.

> Накладные расходы на «зеркало» превысят всю возможную мышиную возню на React. React работает с VDom. А зеркало это реальный DOM. Разница в производительности — на порядки.

Вы не сможете получить реальные размеры текста не отрендерив его. Про проблему отображения текста я писал выше.
> Добавьте к этому то, что реальный размер для зеркала вы получите только, зная реальное место, куда его поместить.
В это случае это не нужно. Вы его рендерите считываете размеры и убираете или прячете.
> Самое время переписать его задействовав двойной рендер :) Поймите, операции с DOM, такие как reflow ОЧЕНЬ дорогие
Это все понятно.
> (в вашем случае render VDOM для одного v-тега)
Не получите вы реальный размер текста в VDOM.
Не получите вы реальный размер текста в VDOM.

Вы получите его в componentDidMount без всяких зеркал, прятаний и прочих шаманств. Отрендерили сколько надо, получили размер, дорендерили с нюансами. И даже моргать ничего не будет.

Смотрите. Изначальный посыл был не менять state в componentDidMount, так как его изменения заставят перерендериться компонент и если, например, компонент рендерит в дом 1000 компонентов, то вы сильно замедлите итоговый рендеринг.
Тот вариант который описывал я и который предлагаете вы, вообще из другой оперы. Он не требует сохранения чего бы то ни было в state. Он маленький. Ссылку на компонент вы сохраняете в переменной класса. В componentDidMount вы получаете размер компонента по ссылке. Далее с этой информаций вы можете делать что хотите. Вы можете запросить эти данные из другого компонента, вы можете поменять в итоге state и перерендерить этот маленький компонент. Что угодно. Это будет в итоге не существенно. Но вот если вы таким образом будете рендерить 1000 компонентов и все их потом перерендеривать, это будет уже существенно и разницу вы заметите.
Изначальный посыл был не менять state в componentDidMount, … если, например, компонент рендерит в дом 1000 компонентов, то вы сильно замедлите итоговый рендеринг

Давайте начнём с того, что если 1 компонент рендерит целую 1000, то у вас что-то не так с архитектурой приложения. Что это за компонент такой? Список в 1000 элементов? Если да, то такой список является болезненной зоной для любого фреймворка, пока вы не задействуете тут VirtualDom. Если же дочерних компонент мало, а 1000 набирается при рендере вглубь — то откройте для себя PureComputed-ы. Тут проблема именно в том, что НЕ должно быть такого компонента, update которого впустую затрагивает 1000-у других компонент. Это проблемы с архитектурой вашего React приложения. И вместо того, чтобы иметь такого монстра, а все update делать "руками" (setAttribute, appendChild, remove и пр.) надо переписать эту часть приложения.


Далее с этой информаций вы можете делать что хотите.

Собственно почти всегда вы делаете именно setState. Потому что именно он вам и нужен. Вы получили какую-то необходимую информацию из DOM, и теперь рендерите компонент снова учитывая её. Такой рендер, при правильной архитектуре — копеечный. Вглубь он не уходит, реконсиляция быстрая, и пара операций над DOM-ом (какие-нибудь аттрибуты или стили выставить). Не нужны тут никакие зеркала.


Всё остальное в вашем комментарии у вас, похоже, базируется на том, что у вас есть некий монстр-пожиратель-ресурсов, который рендерит 1000 компонент и трогать его через React себе дороже.

Не нужны тут никакие зеркала.

Усложним пример. В прочем, мы уже залезли в дебри и крайние случаи. Вам нужно снять размеры с дива, а отрендерить, например, таблицу. Или снять размеры с дива с целью выяснения максимальной высоты строки. Или любых два разных элемента на ваш выбор.


Всё остальное в вашем комментарии у вас, похоже, базируется на том, что у вас есть некий монстр-пожиратель-ресурсов, который рендерит 1000 компонент и трогать его через React себе дороже.

Очень часто люди в componentDidMount меняют данные которые влияют на рендеринг, и делают это, как правило, не в каких то особых случаях, а просто потому, что захотелось. Мой посыл избегать этого и:


You may call setState() immediately in componentDidMount(). It will trigger an extra rendering, but it will happen before the browser updates the screen. This guarantees that even though the render() will be called twice in this case, the user won’t see the intermediate state. Use this pattern with caution because it often causes performance issues.

То что я и хотел донести:
Use this pattern with caution — используйте с осторожностью, потому что — it often causes performance — это часто влияет на производительность.
https://reactjs.org/docs/react-component.html#componentdidmount

Use this pattern with caution

Это ― разумно. Откройте md страницу этого правила (no-did-mount-set-state) там речь про property/layout thrashing, которые и правда нежелательны.


О чём по факту эти правила? Они о том, что нет смысла использовать setState в componentDidMount в тех случаях, когда это можно сделать в конструкторе, этого можно не делать вовсе, это можно сделать через мемоизатор и пр… Т.е. тогда когда человек просто творит какую-то дичь, не поняв, зачем componentDidMount вообще нужен.


Вам нужно снять размеры с дива, а отрендерить, например, таблицу.

Взять размеры одного компонента, чтобы повлиять на размеры какого-либо другого компонента. Так? Странная ситуация, но допустим. В таком случае мы не используем эти размеры в .render методе Div-а. Мы их используем в .render-методе Table-а. И setState будем задавать уже у него. Каким образом будем их связывать — вопрос отдельного топика. Можно через props вышестоящего компонента.


Что из этого всего ^ следует? А вот это и следует. Я с самого начала об этом написал. И да, об этом пишет и документация React-а. И это никак не связано с вашим текстом react/no-did-mount-set-state. Прочитайте тот мой комментарий внимательно, пожалуйста. Это очень простая штука. В React-е нет неопределённости между выбором писать в поле класса, или в state. Это всегда однозначно ясно. И componentDidMount тут не причём.

Это всегда однозначно ясно.

Как показывает практика, к сожалению, не всем. И я очень рад, что мы с вами обсудили данный топик.
Все что влияет на рендер одного компонента в state. Двух и более в props. Остальное в переменные класса. — Для меня это золотое правило которым я руководствуюсь при выборе где мне хранить данные.
Я постараюсь собрать все, о чем мы с вами здесь говорили и уточнить react/no-did-mount-set-state, раз он вызвал столько неоднозначности у читающих.

Думаю, следовать большинству правил, поможет грамотно сконфигурированный в IDE линтер.
Кажется все перечисленные правила есть тут, и детектируемы линтерами.
Я, в своих проектах, использую eslint-config-airbnb. Это набор правил для стайлгайда airbnb и airbnb-react.

Airbnb, в целом, сделала большую работу по агригации полезных eslint правил, но я предпочитаю, более строгие варианты в некоторых местах. Исключительно для улучшения чтения кода и дальнейшего его поддерживания.
Кажется все перечисленные правила есть тут, и детектируемы линтерами.

Все верно, но я ставил своей целью не только дать ссылки на эти правила, но и объяснить почему их необходимо выполнять. Надеюсь у меня получилось.
Почему последние годы в js все стали использовать const? Почему не let или var?

const — это константа. То есть переменная, которой значение можно присвоить только один раз.
let — это переменная, которой значение можно присвоить несколько раз.
В сети есть очень много бенчмарков которые показывают сравнение производетельности let, const и var.
Так же рекомендую отличную статью от Эрика
Мои личные рекомендации — использовать только const кроме тех редких случаев когда вам действительно нужна мутация данных (поможет в этом let).

const — это константа. То есть переменная, которой значение можно присвоить только один раз.

Не надо так сильно упрощать объяснения. Народ потом от этого лютует. Никакие это не константы :)


iproger, const в js это странная штука. Написав const a = что_нибудь, мы присваиваем некоему a значение что_нибудь, и в последствии присвоить a что-либо ещё мы не сможем. Это напоминает константы из других языков. Но по факту, внутри этого что_нибудь мы можем что-нибудь поменять. Т.е. по факту константной является только привязка нашего a к данному что_нибудь. Никаких других гарантий слово const не даёт.

faiwer, не надо все усложнять) там MordorReal все правильно написал: const объявляет константную переменную.

а не константное что_нибудьзначение. для значений переменной правила игры остаются без изменений: они либо примитивы (immutable), либо объекты (mutable, с оговорками). все очень просто и это прекрасно.

А вам не кажется, что "константная переменная" это оксюморон? :)

Тут "переменная" не как переменная, а как ссылка на значение.

const в js — это постоянное значение. Значение может быть примитивного(скалярного) или ссылочного (объектного) типа.

Да я в курсе, мне просто кажется, что такие вещи надо "на пальцах" и в примерах показывать, const в JS несколько контр-интуитивны. Особенно если человек пришёл из языка, где const это настоящие константы.

а что ненастоящего в const js? Ссылочный тип? Так скалярное значение ссылки — адрес, и вы его изменить не сможете. Все там интуитивно, просто надо немного low level-а в голове держать.
UFO just landed and posted this here

Ну не знаю. Для меня контринтуитивно когда конструкция типа const user = new User() подразумевает, что мутирующие методы и присваивание свойств объекта класса User мы вызывать не можем. И да, это с других языков типа ассемблера и C, где мы можем константой (#define и подобные) объявить адрес в памяти, изменить его не можем, но память от этого read only не становится — для этого надо дополнительные усилия прилагать, если это вообще возможно в рамках аппаратной архитектуры.

Не var потому что у var причудливая область видимости, hoisting и вот это вот всё, поведение let и const гораздо более предсказуемо и интуитивно понятно.
const, а не let — как правило (хоть и не всегда) переназначение переменной это не очень удачная идея. Можно затереть оригинальное значение и получить какой-нибудь смешной баг. В простом и ясном коде 99.9% переменных не меняют свои значения. Ну и плюс мода на ФП влияет.

UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here

Относитесь к этому как к CodeStyle-у. Ведь у каждой команды свои заморочки. Так и тут. Кто-то предпочитает такие вот дополнительные ограничения, которые, скажем, меня лично, заставляют следить за кодом больше, чем без них. Кому-то и без них прекрасно живётся. Это и правда малозначительная ерунда. Уж если кто-то умудряется писать код в редакторах без подсветки синтаксиса, то, что уж говорить про const-let-var...

UFO just landed and posted this here
UFO just landed and posted this here
> react/forbid-component-props
Передача классов между в компоненты, в случае следовании БЕМ-нотации, обязательна и полезно.
Класс компонента определяет его как Блок, класс передаваемый ему из-вне — как Элемент.
Их свойства не пересекаются, и никогда не конфликтуют.

> react/no-array-index-key
Очень часто у «данных» нет ID. Тут поможет библиотека типа react-uid, которая превратить в key сами данные.
react-uid

Я пока открывал ссылку подумал, что там внутри сериализация. Но нет, там её нет. Интересное решение. Использует внутри себя WeakMap (он не полифилится). Т.е. годится только для immutable-значений. И гарантировано убъёт существующий экземпляр компоненты, если ссылка на объект поменялась. Штука спорная, но решение интересное ;)

WeakMap полифилиться без проблем и есть в core-js, хотя уже в IE11 он нативный.
Насчет immutable — все верно. Если ключа нет, и ключем является обьект — его изменение изменит ключ. На безрыбье и рак — рыба.
WeakMap полифилиться без проблем и есть в core-js

Оно? ( https://github.com/polygonplanet/weakmap-polyfill ). По сути тут мутация за счёт применения defineProperty к самому объекту. Я бы не назвал это "без проблем", но на худой случай сгодится. Или есть какие-то более хитрые решения?

Все известные мне полифилы построены на этом принципе — использование переданного обьекта для хранение значения.
Есть вырожденный случай для IE(IE8) конкретно для DOM обьектов, где для них _есть_ WeakMap (а для не-DOM его нет).

Статья очень не понравилась. Потому что переполнена спорными пунктами.


react/forbid-component-props

Не понял что именно имеется ввиду. Но судя по участку кода, предлагается css-классы назначать внутри дочернего компонента, исходя из переданных ему bool-значений. Так вот. Это разумно. Но только когда наш внутренний компонент "в курсе" всей этой ситуации. А когда он простая пешка, какой-нибудь UI примитив, туда просто глупо передавать всю эту бизнес-логику, и все классы для него нужно подготовить оттуда, где этой логике место быть.


В общем, как обычно, it depends. Никакая это не "распространённая ошибка". Надо просто понимать суть вещей, и тогда ясно, когда и что применить.


react/forbid-prop-types

Опять же it depends. В сложных проектах частое явление, когда какие-нибудь props прокидываются на уровень ниже, где могут быть расфасованы вообще по разным компонентам, с разными API. И тут нам на помощь приходят эти самые .any, .object и пр… А наиболее точные декларации описаны на том уровне, где они по факту задействуются.


Тоже никакая это не ошибка, если человек понимает, что делает.


react/no-access-state-in-setstate

Пожалуйста, уберите оттуда prevState.counter++ и пр., а то глаза кровоточат. Зачем вы меняете поле counter в prevState ради задания нового counter в newState? Строчку сэкономили? Вы понимаете принцип работы ++ i и i ++? Это мутирующие операции. Да, здесь, возможно, это не приведёт к проблемам, но оно вот вам надо?


react/no-array-index-key

На моей практике в 90+% случаев использование индексов в качестве key более, чем оправдано. Перестаньте уже писать это из статьи в статью. Ей богу, ну сколько можно. Доходит до того, что новички, начитавшись этих "ужасов" начинают генерировать ID-ки там где этого делать не надо, сильно усложняя систему. А по факту, если у вас коллекция не предусматривает каких-либо смещений, удалений, добавлений и пр. внутри себя, то боятся совершенно нечего. Мне кажется, таких коллекций большинство. И да, почти всегда в них нет никаких id и др. полей, к которым можно прицепиться.


Очень важно понимать назначение key, и уметь им правильно пользоваться (это крутой инструмент, на самом деле). Но сеять панику и писать "избегайте этой ошибки в будущем" точно излишне. Надо понимать работу механизма, а не просто чего-то зачем-то избегать, и усложнять себе этим жизнь.


react/no-did-mount-set-state

Рекомендую перечитать документацию и прочитать когда надо использовать поля класса, а когда нет. Мне кажется, вы не совсем понимаете этого. И да, в ряде случаев дополнительный вызов render более, чем обоснован. Выше в комментариях я расписал более детально.


react/no-direct-mutation-state

Мутация state и правда зло. Поэтому уберите плз тот кусок кода с ++, пожалуйста :) А immutable-js очень уж на любителя.


react/prefer-stateless-function

Не использую в своём коде (почти) функциональной нотации компоненты. Вижу это очень неудобным в большинстве случаев, т.к. с течением времени компоненты усложняются и приходится сильно корёжить старый код. Поэтому сразу пишу в классах (к тому же использую только PureComputed). Зачем вы такой подход описываете, как ошибку мне не понять. К тому же сами же пишете, что для всех компонент, где есть props вы задаёте propTypes. Т.е. вы ниже всё равно задаёте MyComponent.propTypes =. Стало быть ради чего вы писали стрелочную функцию вместо класса? Потом её ещё и pure() обернёте, да? :)


Нет, можно и так и так. Никаких проблем тут нет. На вкус и цвет, как говорится, все фломастеры разные. Но ошибки тут точно никакой нет. Уберите этот пункт совсем, зачем он?


react/jsx-no-bind

Самая большая ошибка из всех. И ей уделён всего 1 абзац. Странно вы приоритеты расположили. Благо об bind проблеме не писал уже разве что только ленивый и материалов навалом.


На мой взгляд эта статья полна предрассудков и субъективщины. А подаётся это всё как типовые ошибки.

Спасибо за вашу критику.
> На мой взгляд эта статья полна предрассудков и субъективщины.
собственно как и ваш комментарий :)
Я описал свой личный опыт и те проблемы с которыми столкнулся лично я. Вы, возможно, еще их не видели, а может просто не обратили на них внимание.
> Пожалуйста, уберите оттуда prevState.counter++
Спасибо. Поправил. Действительно проглядел данный кусок кода.
собственно как и ваш комментарий

Дык, а давайте по делу. Пункт за пунктом. Просто это вы, а не я, написали статью в менторском тоне, для "молодых разработчиков", куда записали большое кол-во противоречивых моментов. Вы бы туда ещё табы-vs-пробелы затолкали, ей богу :)


В то время как реальных типовых ошибок я в статье почти не вижу. Скажем где совершенно регулярная ошибка: городить большие шибко-умные компоненты, вместо множества мелких? Где что-нибудь про props-hell? Где про хардкод? Где про разделение обязанностей (скажем fetch-и где попало)? Где про не-чистые render-методы? Новички правда любят в render что-нибудь мутировать. Ну и т.д… Зато первым же пунктом пишете про не передавайте в нижестоящий компонент className :) WAT?

Я и не претендую быть истиной в одной инстанции. Я поделился своим мнением и опытом. Ваше право не согласиться и оспорить его. В споре, как говорится, рождается истина.
Вы написали, что моя статья субъективна. Она субъективна и я это не отрицаю. Вы описали ваши замечания в комментарии. Это ваши замечания которые ВЫ считаете правильными. То-есть они субъективны.
К сожаление, у меня нет много свободного времени для описания всех имеющихся проблем в Реакте. Я описал только самые популярные на мой взгляд. Для этих проблем не глупые люди написали даже отдельные правила для eslint (надеюсь, вы не будете отрицать, что если их написали и добавили в eslint, значит они имеют место быть и даже считаются совсем не субъективными проблемами). Если у вас много опыта и есть время, welcome. Я с удовольствием почитаю про ваш опыт и проблемы в статье на хабре.

Для этих проблем не глупые люди написали даже отдельные правила для eslint

А у них справка есть? :) В eslint-е огромное кол-во правил. Они даже конфликтуют с друг другом, если вы их одновременно все включите. К тому же не забывайте, что при надобности их отключают условным комментарием.


значит они имеют место быть и даже считаются совсем не субъективными проблемами

ROFL, шутка дня. Вы похоже ооочень далеки от мира eslint :)

Что то вы отшутились на мои комментарии :) Ну тогда и я могу посмеяться :)
> Первая распространенная ошибка это передача 'style' и 'className' как props в ваш компонент.
И ссылка на статью 2015 года. На дворе 2018. Для корректной работы CSSinJS (styled components, JSS etc.) необходимо прокидывать className в свой компонент.

Более правильным вариантом будет при помощи того же classnames склеивать приходящий в виде пропсов className с собственными классами компонента.

> Не Используйте Bind И Arrow Function В Jsx. Никогда. Больше.
Это слишком категоричное заявление. Сколько копий уже сломано на эту тему. Я просто оставлю эту ссылку здесь.

TL;DR в моем вольном переводе
Преждевременная оптимизация — корень всех зол. Если вы не делаете измерения производительности, вы даже не знаете, работает ли ваша оптимизация, и вы точно не знаете, не сделала ли она только хуже.
Я просто оставлю эту ссылку здесь.

Пожалуйста, не надо её нигде оставлять. Лучше её закопать.

Это мнение, как и все другие, имеет право на существование, давайте не будем опять начинать тот срач. Ну вы в курсе, какой.

Нет, я не в курсе что за срач. И что это за мнение такое, что его критиковать нельзя. Статья Ryan Florence подана как поучительная, да ещё и c "Premature optimization is the root of all evil". А на деле несёт большое зло. Ведь кто-то ею даже руководствуется. И потом на каждый чих, на каждый action мы получаем rerender огромных кусков VDOM и всё тормозит. Избегая написания () => { впользу (){ автор плодит неконтролируемые тормоза. И учит этому других. Полагаю, он просто ещё не столкнулся ни с одним маломальски большим приложением. Не смотрел его flame graph, схватившись за голову обеими руками.

На самом деле все не так страшно — если уж что-то начало апдейт дерева, и добавляет всюду новые onClick — ну возможно можно использовать более «интересные» решения чтобы распространение этого апдейта прекратить.
Да я помню этот комментарий, справедливо.
Весь код написан в ES6 стиле, поэтому, что бы повторить его вам нужно использовать Babel в вашем проекте (а еще есть такие кто его не использует?).


Конечно есть такие кто не использует Babel, есть ведь TypeScript.
Sign up to leave a comment.

Articles