📜 ⬆️ ⬇️

React cheatsheet or a couple of life hacks



I will share several practices that I use when creating React-components. Interested please under the cat.

Setting parameters by condition


Take for example the button and its particular states - size and color.

Usually in the code I meet something like this:
')
import React from 'react'; function Button({ size, skin, children }) { return ( <button className={`button${size ? ` button_size_${size}` : ''}${skin ? ` button_skin_${skin}` : '' }`}> {children} </button> ); } 

Its readability seems to be preserved, but what if we have even more states?

I thought that it was much easier to collect all the available states into a collection, where the key would be the name of the state, and the value would be the name of the class. Easy viewing, convenient use. In addition, we will save on operations with strings.

So:

 import React from 'react'; import classNames from 'classnames'; const SIZE_CLASSES = { small: 'button_size_small', medium: 'button_size_medium', large: 'button_size_large', }; const SKIN_CLASSES = { accent: 'button_skin_accent', primary: 'button_skin_primary', }; function Button({ size, skin, children }) { return ( <button className={classNames( 'button', SIZE_CLASSES[size], SKIN_CLASSES[skin], )} > {children} </button> ); } 

For the convenience of assigning classes, I use the classnames utility.

Let’s write another example.

 import React from 'react'; const RESULT_IMAGES = { 1: '/img/medal_gold.svg', 2: '/img/medal_silver.svg', 3: '/img/medal_bronze.svg', }; function Result({ position }) { return ( <div> <img src={RESULT_IMAGES[position]} /> <h2>!   {position} !</h2> </div> ); } 

Tag setting by condition


Sometimes there is a need to expose a particular HTML tag or React component when rendering, depending on the condition. For example, of course, let's take our favorite button, because it perfectly demonstrates the problem. From the point of view of the UI, it usually looks like a button, but inside, based on the situation, it can be either a <button /> tag or an <a /> tag.

If we are not afraid of repetition of the code, then we can conditionally return a specific wrapper with the transfer of all the necessary parameters to it or, in the end, use React.cloneElement . For example:

 import React from 'react'; function Button({ container, href, type, children }) { let resultContainer = null; if (React.isValidElement(container)) { resultContainer = container; } else if (href) { resultContainer = <a href={href} /> } else { resultContainer = <button type={type} /> } return React.cloneElement( resultContainer, { className: 'button' }, children, ); } Button.defaultProps = { container: null, href: null, type: null, }; 

But I’m more impressed with the definition of a variable through an uppercase letter .

 import React from 'react'; function Button({ container, href, type, children }) { let Tag = null; if (React.isValidElement(container)) { Tag = container; } else if (href) { Tag = 'a'; } else { Tag = 'button'; } return ( <Tag href={href} type={type} className="button"> {children} </Tag> ); } Button.defaultProps = { container: null, href: null, type: null, }; 

Change direction of elements


Take for example the strip of life from the games. On the left is our player, on the right his opponent We confine ourselves to the fact that everyone will have an avatar and a name. The order of our player avatar-name, the opponent - the name-avatar. To determine the direction we will use the direction parameter.

Consider three ways.

Method 1. Assignment to the appropriate variables by condition.


 import React from 'react'; function Player({ avatar, name, direction }) { let pref = null; let posf = null; if (direction === 'ltr') { pref = <img class="player__avatar" src={avatar} alt="Player avatar" />; posf = <span class="player__name">{name}</span>; } else { pref = <span class="player__name">{name}</span>; posf = <img class="player__avatar" src={avatar} alt="Player avatar" />; } return ( <div className="player"> {pref} {posf} </div> ); } Player.defaultProps = { direction: 'ltr', }; 

Method 2. Array.prototype.reverse


 import React from 'react'; function Player({ avatar, name, direction }) { const arrayOfPlayerItem = [ <img key="avatar" class="player__avatar" src={avatar} alt="Player avatar" />, <span key="name" class="player__name">{name}</span>, ]; if (direction === 'rtl') { arrayOfPlayerItem.reverse(); } return ( <div className="player"> {arrayOfPlayerItem} </div> ); } Player.defaultProps = { direction: 'ltr', }; 

Method 3. Manipulation through CSS.


All we need is to assign the desired class.

 import React from 'react'; import classNames from 'classnames'; const DIRECTION_CLASSES = { ltr: 'player_direction_ltr', rtl: 'player_direction_rtl', }; function Player({ avatar, name, direction }) { return ( <div className={classNames( 'player', DIRECTION_CLASSES[direction], )} > <img class="player__avatar" src={avatar} alt="Player avatar" /> <span class="player__name">{name}</span> </div> ); } Player.defaultProps = { direction: 'ltr', }; 

Then, using CSS, there are a lot of ways to solve the problem:

 // 1. flexbox .player { display: flex; } .player_direction_rtl .player__avatar { order: 1; } .player_direction_rtl .player__name { order: 0; } // 2. direction .player, .player__avatar, .player__name { display: inline-block; } .player_direction_rtl { direction: rtl; } // 3. float .player { display: inline-block; } .player_direction_rtl .player__avatar, .player_direction_rtl .player__name { float: right; } 

The only disadvantage is that the user, if he tries to select the text with the mouse, will receive cognitive dissonance, since in fact we do not change the location of the elements in the DOM tree.

Saving the link to the DOM element


I use the following template for this task.

 import React, { Component } from 'react'; class Banner extends Component { componentDidMount() { if (this.DOM.root) { this.DOM.root.addEventListener('transitionend', ...); } } handleRefOfRoot = (node) => { this.DOM.root = node; }; DOM = { root: null, }; render() { return ( <div className="banner" ref={this.handleRefOfRoot}> {this.props.children} </div> ); } } 

Instead of declaring a function directly in ref , I bring it to a method. Due to this, when rendering, a new function is not created, and the creation of an arrow function eliminates the need to bind the context through the bind method. Important: you should do this only if you are sure that your component will be called several times in a short period of time, otherwise it is better to use the ref={(node) => { this.DOM.<node_name> = node; }} ref={(node) => { this.DOM.<node_name> = node; }} so that the page memory is not loaded again.

Saving nodes to a DOM object is an analogy to the already obsolete refs field of a stateful component. Conveniently, when everything is stored in one place.

In order to avoid errors, I recommend checking for the presence of a DOM node before using it, to be sure of getting a link to it, that is, that it is not null .

At last


Here are some more articles (old and not so) from the “Best Practices” series:

  1. "React Patterns", RUVDS.com
  2. Design Patterns in React, RUVDS.com
  3. "Our Best Practices for Writing React Components", Scott Domes
  4. "React.js pure render performance anti-pattern", Esa-Matti Suuronen

I would also be happy if you share your best practices in the comments.

Source: https://habr.com/ru/post/354056/


All Articles