Самые распространенные ошибки в вашем React коде, которые вы (возможно) делаете

в 12:24, , рубрики: javascript, jsx, React, react.js, ReactJS

Самые распространенные ошибки в вашем React коде, которые вы (возможно) делаете - 1

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

Весь код написан в ES6 стиле, поэтому, что бы повторить его вам нужно использовать Babel в вашем проекте (а еще есть такие кто его не использует?).

Я постарался объяснить как можно подробнее каждую ошибку, поэтому, моя статья больше ориентирована на молодых разработчиков, которые еще находятся в поиске новых знаний. Хотя, как мне кажется, и опытный разработчик может найти для себя пару интересных вещей в этой статье.

Если вам интересно, то добро пожаловать под кат.

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

(react/forbid-component-props)

Первая распространенная ошибка это передача 'style' и 'className' как props в ваш компонент. Избегайте этого, так как вы добавите много сложности в ваши компоненты.

Вместо это можно использовать библиотеку 'classnames' и добавить интересные вариации в ваш компонент (если вы используете css классы):

const { hasError, hasBorder } = this.props;
const componentClasses = classnames({
    'your-component-main-class': true,
    'your-component-main-class_with-error': hasError,
    'your-component-main-class_with-border': hasBorder,
});

(react/forbid-prop-types)

Следующая ошибка — не информативные propTypes. Не используйте PropTypes.any, PropTypes.array и PropTypes.object. Описывайте ваши props как можно подробнее. Это позволит вам оставить хорошую документацию на будущее, и вы (или другой разработчик) еще не раз скажите себе большое спасибо.

class MyComponent extends React.Component {
    static propTypes = {
        user: PropTypes.shape({
            id: PropTypes.number,
            name: PropTypes.string,
        }),
        policies: PropTypes.arrayOf(PropTypes.shape({
            id: PropTypes.number,
            type: PropTypes.string,
            value: PropTypes.string,
        }),
    }
}

(react/forbid-foreign-prop-types)

Давайте продолжим с propTypes. Не используйте propTypes другого компонента:

import SomeComponent from './SomeComponent';
SomeComponent.propTypes;

Создайте файл в котором вы будете содержать в порядки ваши глобальные propTypes:

import { userShape, policiesArray } from '../common/global_prop_types';

Это поможет babel-plugin-transform-react-remove-prop-types убрать propTypes из продакшен кода и сделать ваше предложение чуточку легче.

(react/no-access-state-in-setstate)

Следующая ошибка очень интересная:

class MyComponent extends React.Component {
    state = {
        counter: 1,
    };

    incrementCounter = () => this.setState({ counter: this.state.counter++ });
    massIncrement = () => {
        // this code will lead to not what you expect
        this.incrementCounter();
        this.incrementCounter();
    }
}

Потому что setState это асинхронная функция состояния state в обоих случаях будет одинаковым.
this.state.counter будет равен 1 и мы получим:

incrementCounter = () => this.setState({ counter: 1++ });
incrementCounter = () => this.setState({ counter: 1++ });

Для того, что бы это избежать можно использовать setState callback который получает как аргумент прошлое состояние state:

incrementCounter = () => this.setState((prevState) => ({ counter: prevState.counter++ }));

(react/no-array-index-key)

Эта ошибка так же очень часто встречается, поэтому, прочтите внимательно и избегайте ее в будущем:

users.map((user, index) => (
  <UserComponent {...user} key={index}  />
));

React использует prop key как ссылку к DOM элементу, и это помогает ему быстро найти и отрендерить нужный компонент (все, конечно, сложнее, но я упростил специально).
Что случиться если вы добавите нового юзера в середину массива? React будет вынужден перерендерить все UserComponents после добавленного нового, так как индекс будет изменен для большого кол-ва компонентов. Используйте уникальные ключи вместо этого. Очень простой выход это id, которые вы получаете из вашей базы данных:

users.map((user) => (
  <UserComponent {...user} key={user.id}  />
));

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

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

(react/no-direct-mutation-state)

Мутация state это очень большая ошибка. Неконтролируемая мутация state приведет к необнаруживаемым багам и, как следствие, к большим проблемам. Мое персональное мнение это использование immutable-js, как библиотеку, которая добавляет иммутабельные структуры. И их вы можете использовать с Redux/MobX/Любой библиотекой state менеджмента. Так же вы можете использовать deepClone из lodash для клонирования state и последующей мутации клона или использовать новую фичу JS — деструкцию (destructuring):

updateStateWrong = () => this.state.imRambo = true;

updateStateRight = () => {
    const clonedState = cloneDeep(this.state);
    clonedState.imAGoodMan = true;
    this.setState(clonedState); 
}

updateWithImmutabelJS = () => {
    const newState = this.state.set('iUseImmutableStructure', true);
    this.setState(newState);
}

updateWithDestructuring = () => this.setState({ ...this.state, iUseDestructuring: true });

(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>

(react/prop-types)

Пожалуйста, всегда добавляйте проверку на типы props (propTypes) если ваш компонент получает props. Думайте об этом как о документировании вашего кода. Вы еще не раз скажете себе 'спасибо' за это (а может быть и мне :)). PropTypes поможет вам понять и разобраться, что ваш компонент может отрендерить, а так же, что ему нужно для рендеринга.

MyPureComponent.propTypes = {
    id: PropTypes.number.isRequired, // And I know that without id component will not render at all, and this is good.
}

(react/jsx-no-bind)

Очень распространенная и большая ошибка которую я видел в коде много раз. Не Используйте Bind И Arrow Function В Jsx. Никогда. Больше.
Самое жаркое место в аду ждет того кто пишет .bind(this) в JSX.
Каждый раз когда компонент рендериться ваша функция будет создаваться заново, и это может сильно затормозить ваше приложение (это связано с тем, что garbage collector будет вынужден запускаться значительно чаще). Вместо .bind(this) вы можете использовать Arrow functions определенным образом:

class RightWayToCallFunctionsInRender extends React.Component {
    handleDivClick = (event) => event;
    render() {
        return <div onClick={this.handleDivClick} />
    }
}

const handleDivClick = (event) => event;
const AndInPureComponent = () => {
    return <div onClick={handleDivClick} />
}

(react/jsx-no-target-blank)

Ошибка связанная с безопасностью. Для меня выглядит очень странно, что люди до сих пор делают эту ошибку. Очень много людей написало очень много статей на эту тему в 2017.
Если вы создаете ссылку с target='_blank' атрибутом не забудьте добавить к ней rel='noreferrer noopener'. Очень просто:

<a href="https://example.com" target="_blank" rel="noreferrer noopener" />

Спасибо вам!

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

Автор: MordorReal

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js