The original:
Clean Code vs. Dirty Code: React Best Practices


By Donavon West

This article focuses on clean code practices for modern React software development, along with some useful “syntax candy” from ES6/ES2015.

What is clean code, and why do you care?

Clean code represents a consistent coding style that makes code easier to write, read, and maintain. It is common for developers to issue a Merge Request as soon as the problem is resolved when solving a problem. But I don’t think the work is really done at this point, and we can’t just be satisfied that the code works.

This is actually the best time to clean up your code, keeping it maintainable by removing dead code (zombie code), refactoring, and removing uncommented code. Ask yourself, “Will other people understand this code six months from now?” In short, you should be proud to show others the code you write.

And why should it matter? Because we often say that a good developer is “lazy”. In situations where they need to do something over and over again, they find an automated (or better) solution to do it.

Clean code passes the “taste test”

Clean code should pass the “taste test.” What does that mean? We look at code, our own or someone else’s, and we say, “There’s something wrong here.” If it doesn’t feel right, there may be a real problem. If you feel like you’re trying to fit a square nail into a round hole, pause and take a break. After many attempts, you will find a better solution.

Clean code is DRY compliant

DRY is an acronym that means “Don’t Repeat Yourself.” If you find that multiple places are doing the same thing, this is a good time to merge duplicate code. If you see patterns in your code, you need to be DRY.

// Dirty
const MyComponent = () => (
  <div>
    <OtherComponent type="a" className="colorful" foo={123} bar={456} />
    <OtherComponent type="b" className="colorful" foo={123} bar={456} />    
  </div>
); 
Copy the code
// Clean
const MyOtherComponent = ({ type }) => (
  <OtherComponent type={type} className="colorful" foo={123} bar={456} />
);
const MyComponent = () => (
  <div>
    <MyOtherComponent type="a" />
    <MyOtherComponent type="b" />
  </div>
);
Copy the code

Sometimes, as in the example above, implementing the DRY principle can actually increase the amount of code. However, DRY also generally improves the maintainability of your code.

Note that it’s easy to fall into the trap of overusing the DRY principle, and you should learn to overdo it.

Clean code is predictable and testable

Writing unit tests is not just a good idea, it should be mandatory. Otherwise, how can you be sure that new features won’t cause bugs elsewhere?

Many React developers choose Jest as a zero-configuration test runner and then generate code coverage reports. If you’re interested in the before-and-after visualization, check out American Express’s Jest Image Snanshot.

Clean code is self-commented

Has this happened before? You write some code and include detailed comments. Then you find a bug and go back and fix the code. But did you change the comments to reflect the new logic? Maybe, maybe not. The next person who looks at your code may fall into a trap by noticing these comments.

Comments are meant only to explain complex ideas; that is, don’t comment code that is obvious. At the same time, fewer comments reduce visual distractions.

// Dirty
const fetchUser = (id) => (
  fetch(buildUri`/users/${id}`) // Get User DTO record from REST API
    .then(convertFormat) // Convert to snakeCase
    .then(validateUser) // Make sure the the user is valid
); 
Copy the code

In the clean code version, we renamed some functions to better describe their functionality, eliminating the need for comments and reducing visual distractions. And avoid subsequent confusion caused by mismatches between code and comments.

// Clean
const fetchUser = (id) => (
  fetch(buildUri`/users/${id}`)
    .then(snakeToCamelCase)
    .then(validateUser)
); 
Copy the code

named

In my previous article, using functions as child components is an anti-pattern that emphasizes the importance of naming. Every developer should carefully consider variable names, function names, and even file names.

Here are the naming principles:

  • Boolean variables or functions that return Boolean values should begin with “is”, “has”, or “should”.
// Dirty
const done = current >= goal;

// Clean
const isComplete = current >= goal; 
Copy the code
  • Function names should reflect what is done, not how it is done. In other words, don’t include implementation details in your naming. If something changes one day, there is no need to refactor the code that references the function. For example, configuration might be loaded from a REST API today, but it might be written directly into JavaScript tomorrow.
// Dirty
const loadConfigFromServer = () => {
  ...
}; 

// Clean
const loadConfig = () => {
  ...
};
Copy the code

Clean code follows mature design patterns and best practices

Computers have been around for a long time. Over the years, programmers have discovered a set of patterns, called design patterns, by solving specific problems. In other words, some algorithms have been proven to work, so you should stand on the shoulders of others and avoid making the same mistakes.

So what are best practices? Similar to design patterns, but more broadly applicable than just coding algorithms. For example, “code should be statically checked” or “React as a peerDependency when writing a library” could be best practices.

When building React applications, you should follow the following best practices:

  • Use small functions, each with a Single function, known as the Single responsibility principle. Make sure each function does a job and does it well. This allows complex components to be broken down into many smaller components. At the same time, there will be better testability.
  • Beware of leaky Abstractions. In other words, don’t force consumers to understand the internal code implementation details.
  • Follow strict code inspection rules. This will help you write clean, consistent code.

Clean code doesn’t have to take long to write

It’s often said that writing clean code reduces productivity. It’s nonsense. Yes, you may need to slow down at first, but eventually the pace will pick up as you write less code.

Also, don’t underestimate the refactoring that code reviews can lead to and the time it takes to fix problems. If you break your code into smaller modules, each with a single responsibility, you’ll probably never have to touch most of them again. Time is saved, so “write it and forget it.”

Examples of bad code and clean code

Use the DRY principle

Take a look at the following code example. As mentioned above, step back from your monitor and notice any patterns? Note that the Thingie component is almost identical to the ThingieWithTitle component except for the Title component, which is the best case for the DRY principle.

// Dirty
import Title from './Title';

export const Thingie = ({ description }) => (
  <div class="thingie">
    <div class="description-wrapper">
      <Description value={description} />
    </div>
  </div>
);

export const ThingieWithTitle = ({ title, description }) => (
  <div>
    <Title value={title} />
    <div class="description-wrapper">
      <Description value={description} />
    </div>
  </div>
);
Copy the code

Here, we pass children to Thingie. Then create ThingieWithTitle, which contains Thingie and passes the Title as its child to Thingie.

// Clean import Title from './Title'; export const Thingie = ({ description, children }) => ( <div class="thingie"> {children} <div class="description-wrapper"> <Description value={description} /> </div> </div> ); export const ThingieWithTitle = ({ title, ... others }) => ( <Thingie {... others}> <Title value={title} /> </Thingie> );Copy the code

The default value

Look at the code below. Using logic or setting the default value of className to “icon-large” looks like code written in the last century.

// Dirty
const Icon = ({ className, onClick }) => {
  const additionalClasses = className || 'icon-large';
  
  return (
    <span
      className={`icon-hover ${additionalClasses}`}
      onClick={onClick}>
    </span>
  );
}; 
Copy the code

Here we use ES6’s default syntax to replace the value of undefined, and we can use ES6’s arrow function expressions to write them in a single statement, removing the dependency on return.

// Clean
const Icon = ({ className = 'icon-large', onClick }) => (
  <span className={`icon-hover ${className}`} onClick={onClick} />
); 
Copy the code

In the cleaner version below, use the React API to set the default values.

// Cleaner
const Icon = ({ className, onClick }) => (
  <span className={`icon-hover ${className}`} onClick={onClick} />
);

Icon.defaultProps = {
  className: 'icon-large',
}; 
Copy the code

Why is it cleaner? And will it really be better? Aren’t all three versions doing the same thing? In a sense, yes. The benefit of having React set prop defaults is that it produces more efficient code and allows checking of defaults through propTypes in class-based lifecycle components. Another advantage is that the default logic is removed from the component itself.

For example, you can do the following to put all the default attributes in one place. Of course, I’m not suggesting that you do this, just that you have flexibility.

import defaultProps from './defaultProps';
// ...
Icon.defaultProps = defaultProps.Icon; 
Copy the code

Separate the stateful parts from the render

Mixing stateful data loading logic with rendering logic can increase component complexity. A better approach is to write a stateful container component that does the data loading, and then write another component that displays the data. This is called the container pattern.

In the example below, the user data load and display capabilities are placed in one component.

// Dirty class User extends Component { state = { loading: true }; render() { const { loading, user } = this.state; return loading ? <div>Loading... </div> : <div> <div> First name: {user.firstName} </div> <div> First name: {user.lastName} </div> ... </div>; } componentDidMount() { fetchUser(this.props.id) .then((user) => { this.setState({ loading: false, user })}) } }Copy the code

In the clean version, loading data and displaying data are separated. This not only makes the code easier to understand, but also reduces the amount of testing because each part can be tested independently. And because RenderUser is a stateless component, the results are predictable.

// Clean
import RenderUser from './RenderUser';

class User extends Component {
  state = { loading: true };

  render() {
    const { loading, user } = this.state;
    return loading ? <Loading /> : <RenderUser user={user} />;
  }

  componentDidMount() {
    fetchUser(this.props.id)
      .then(user => { this.setState({ loading: false, user })})
  }
} 
Copy the code

Use stateless components

React V0.14.0 introduced the stateless function component (SFC), which was simplified to a pure render component, but some developers are still using it the old way. For example, the following components should be converted to SFC.

// Dirty class TableRowWrapper extends Component { render() { return ( <tr> {this.props.children} </tr> ); }}Copy the code

The clean version removes much of the information that could cause interference. With the React core optimization, using stateless components takes up less memory because no Component instances are created.

// Clean
const TableRowWrapper = ({ children }) => (
  <tr>
    {children}
  </tr>
); 
Copy the code

Residual/Extended Attributes (REST/Spread)

About a year ago, I also recommended using object.assign. But times are changing fast, with the introduction of a new feature in ES2016/ES7, REST/Spread.

For example, if you pass some props to a component, you only want to use className in the component itself, but you need to pass all the other props to the child components. Here’s what you might do:

// Dirty
const MyComponent = (props) => {
  const others = Object.assign({}, props);
  delete others.className;
  
  return (
    <div className={props.className}>
      {React.createElement(MyOtherComponent, others)}
    </div>
  );
}; 
Copy the code

This is not a very elegant solution. But with REST/Spread, it’s easy to do,

// Clean const MyComponent = ({ className, ... others }) => ( <div className={className}> <MyOtherComponent {... others} /> </div> );Copy the code

We expand the remaining properties and pass them to the MyOtherComponent as new props.

Fair use deconstruction

ES6 introduces the concept of destructuring, which is a great feature that uses syntax similar to object or array literals to get the properties of an object or the elements of an array.

Object to deconstruct

In this case, the receiving newProps componentWillReceiveProps component parameters, and then set the active property to the new state. The active.

// Dirty
componentWillReceiveProps(newProps) {
  this.setState({
    active: newProps.active
  });
} 
Copy the code

In the clean version, we deconstruct newProps into active. Not only do we not need to reference newProps. Active, but we can also call setState using ES6’s short properties feature.

 // Clean
componentWillReceiveProps({ active }) {
  this.setState({ active });
}
Copy the code

An array of deconstruction

An oft-overlooked ES6 feature is array deconstruction. Take the following code, which takes the locale value, such as “en-us,” and divides it into language (en) and country (US).

// Dirty
const splitLocale = locale.split('-');
const language = splitLocale[0];
const country = splitLocale[1]; 
Copy the code

In the clean version, this is done automatically using ES6’s array deconstruction feature:

// Clean
const [language, country] = locale.split('-');
Copy the code

So the conclusion is

Hopefully, this article has helped you see the benefits of writing clean code, even using some of the code examples presented here. Once you get used to writing clean code, you’ll soon feel the “write it and forget it” lifestyle.