The most common errors in your React code that you (possibly) make


Inspired by the read articles on the medium, I decided to write my article and tell you how to avoid the most common mistakes in your React application and why it should be done.


All code is written in ES6 style, therefore, to repeat it you need to use Babel in your project (and there are those who do not use it?).


I tried to explain every mistake as much as possible, therefore, my article is more focused on young developers who are still in search of new knowledge. Although, as it seems to me, an experienced developer can find for himself a couple of interesting things in this article.


If it is interesting to you, welcome under kat.


In brackets before each paragraph I left a link to the eslint rule. You can later find them in git and add them to your project.



(react / forbid-component-props)


The first common mistake is passing 'style' and 'className' as props to your component. Avoid this, as you add a lot of complexity to your components.


Instead, you can use the 'classnames' library and add interesting variations to your component (if you use css classes):


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)


The following error is not informative propTypes. Do not use PropTypes.any, PropTypes.array and PropTypes.object. Describe your props as detailed as possible. This will allow you to leave good documentation for the future, and you (or another developer) tell yourself many times more thanks.


classMyComponentextendsReact.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)


Let's continue with propTypes. Do not use propTypes of another component:


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

Create a file in which you will contain your global propTypes in order:


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

This will help babel-plugin-transform-react-remove-prop-types remove propTypes from production code and make your application a little easier.



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


The following error is very interesting:


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

Because setState is an asynchronous state function in both cases will be the same.
this.state.counter will be equal to 1 and we will get:


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

In order to avoid this, you can use setState callback which receives as an argument the past state of the state:


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


(react / no-array-index-key)


This error is also very common, so read carefully and avoid it in the future:


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

React uses the prop key as a link to the DOM element, and this helps it quickly find and render the necessary component (everything is, of course, more complicated, but I have simplified it specifically).
What happens if you add a new user to the middle of the array? React will be forced to re-render all UserComponents after adding a new one, since the index will be changed for a large number of components. Use unique keys instead. A very simple way out is the id that you get from your database:


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


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


This error is also very common in my practice. If you try to update the state in the componentDidUpdate method, you will get an infinite re-render loop. React starts a re-render check when a component changes state or props. If you change the state after the component has been rebuilt in the DOM or has already been updated, you can run the check again and again and again ...
When you update the state in componentDidMount, you can re-render the component one more time, because the function is called once DOM.
If you have a need to update the data exactly after the component's mount, I suggest using class variables:


classMyComponentextendsReact.Component{
   componentDidMount() {
      this.veryImportantDataThatCanBeStoredOnlyAfterMount = 'I'll be back!';
   }
   veryImportantDataThatCanBeStoredOnlyAfterMount = void 0;
    render() {
        return <div />
    }
}


(react / no-direct-mutation-state)


The state mutation is a very big mistake. An uncontrolled state mutation will lead to undetectable bugs and, as a result, to big problems. My personal opinion is the use of immutable-js , as a library that adds immutable structures. And you can use them with Redux / MobX / Any state management library. You can also use deepClone from lodash to clone the state and the subsequent mutation of the clone, or use the new JS feature - destructuring:


updateStateWrong = () => this.state.imRambo = true;
updateStateRight = () => {
    const clonedState = cloneDeep(this.state);
    clonedState.imAGoodMan = true;
    this.setState(clonedState); 
}
updateWithImmutabelJS = () => {
    const newState = this.state.data.set('iUseImmutableStructure', true);
    this.setState(data: newState);
}
updateWithDestructuring = () => this.setState({ ...this.state, iUseDestructuring: true });


(react / prefer-stateless-function)


This rule describes more improvement of your code and application than an error, but I still recommend that you follow this rule. If your component does not use state, make it a stateless component (I like the term 'pure component' more):


classMyComponentWithoutStateextendsReact.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)


Please always add a check for types of props (propTypes) if your component receives props. Think of it as documenting your code. You’ll say thank you for it many times (and maybe me :)). PropTypes helps you understand and figure out what your component can render, as well as what it needs for rendering.


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)


A very common and big mistake I have seen in the code many times. Do Not Use Bind And Arrow Function In Jsx. Never. More.
The hottest place in hell is waiting for someone who writes .bind (this) to JSX in the event handler.
Every time when the component is rendered, your function will be created anew, and this can greatly slow down your application (this is due to the fact that the garbage collector will be forced to run much more often). Instead of .bind (this), you can use Arrow functions in a specific way:


classRightWayToCallFunctionsInRenderextendsReact.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)


Security error. It looks very strange to me that people still make this mistake. A lot of people wrote a lot of articles on this topic in 2017.
If you create a link with the target = '_ blank' attribute, do not forget to add rel = 'noreferrer noopener' to it. Very simple:


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

Thanks you!


This is all that I would like to tell you in this article. I would be very grateful if you, my readers, leave me your feedback or comment, and share in it your opinion on the problems that I have described in this article. Also, you can tell me about my and your errors in the code and about everything that you consider necessary to tell. Thank you again!


Also popular now: