Take Your Code to The Other Side

Valentsea
Total
0
Shares

In today’s adventure I will take you to the Other Side of code development. As ONE, we’ll explore the art of code refactoring and see how it can revolutionize your codebase. Sounds marvelous!!! After going through this adventure, you’ll learn how to transform your code from Good to Great or Great to Next level. You’re a developer and it’s your responsibility to take your application to new heights of performance, scalability and maintainability.

Take Your Code to The Other Side


As software projects grow, it’s common for the codebase to become complex & difficult to manage. At this point, code refactoring comes into light from the darkness. It’s the process of improving existing code by restructuring it in a more efficient and effective way without changing its behavior.

Refactoring does not add any new features or functionalities to the software or product. It can improve the quality, readability and maintainability of your codebase. Which makes it easier to work with in the long run.




Why Refactor Your Code?

Why Refactor Your Code

Improved Code Quality:

Refactoring improves code quality by reducing code complexity, removing duplication and improving the overall design of the code. This ends in code that is easier to read and understand, reducing the risk of introducing errors or bugs.

Increased Maintainability:

By reducing code complexity and improving the design of your code, refactoring makes it easier to maintain and update your code in the long term. This can save time and effort for developers working on the project and reduce the risk of introducing bugs when making changes.

Better Performance:

Refactoring can help improve the performance of your code by optimizing algorithms, removing unnecessary code and reducing resource usage. This can lead to faster execution times and improved efficiency.

Easier Collaboration:

By improving code quality and reducing complexity, refactoring can make it easier for developers to collaborate on a project. This can improve team productivity and reduce the risk of introducing errors or bugs when working on shared code.


By moving forward on our adventure, it’s time to perform the code refactoring and break on through to the Other Side.




Code Formatting

It’s important to consider code formatting when working on a team or maintaining a codebase. Different developers may have varying preferences for indentation, line breaks and quotation marks. That ends in an inconsistent code style. Inconsistent code style can make the code look cluttered and challenging to read. So it’s crucial to maintain a consistent code style across the project.

It’s beneficial to use refactoring tools such as Prettier. In case you’re not familiar with Prettier, let’s break it.. Prettier is a popular and user friendly tool that automates code formatting. Once added to the project, it will take care of formatting the code according to pre set style settings. Also you can customize the formatting rules to your preference by creating a .prettierrc file with below configs:

{
  "arrowParens": "always",
  "bracketSameLine": false,
  "bracketSpacing": true,
  "embeddedLanguageFormatting": "auto",
  "htmlWhitespaceSensitivity": "css",
  "insertPragma": false,
  "jsxSingleQuote": false,
  "printWidth": 80,
  "proseWrap": "preserve",
  "quoteProps": "as-needed",
  "requirePragma": false,
  "semi": true,
  "singleAttributePerLine": false,
  "singleQuote": false,
  "tabWidth": 2,
  "trailingComma": "es5",
  "useTabs": false,
  "vueIndentScriptAndStyle": false
}
Enter fullscreen mode

Exit fullscreen mode




Class Component Without State or Lifecycle Methods in React

Original code:

import React, { Component } from "react";

class Adventure extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    console.log("Migration happened");
  }

  render() {
    return (
      <div>
        <p>Break on Through To the Other Sidep>
        <button onClick={this.handleClick}>Migrate mebutton>
      div>
    );
  }
}
Enter fullscreen mode

Exit fullscreen mode

Refactored code:

import React from "react";

function Adventure() {
  const handleClick = () => {
    console.log("Migration happened");
  };

  return (
    <div>
      <p>Break on Through To the Other Sidep>
      <button onClick={handleClick}>Migrate mebutton>
    div>
  );
}
Enter fullscreen mode

Exit fullscreen mode

If the component needs state or lifecycle methods then use class component otherwise use function component.

From React 16.8 with the addition of Hooks, you could use state, lifecycle methods and other features that were only available in class component right in your function component. So, it is always recommended to use Function components, unless you need a React functionality whose Function component equivalent is not present yet, like Error Boundaries.




Avoid extra wrapping

in React

Original code:

import React from "react";

function Adventure() {
  return (
    <div>
      <p>Break on Through To the Other Sidep>
      <button>Migrate mebutton>
    div>
  );
}
Enter fullscreen mode

Exit fullscreen mode

Refactored code:

import React, { Fragment } from "react";

function Adventure() {
  return (
    <Fragment>{/* or use a shorter syntax <>  */}
      <p>Break on Through To the Other Sidep>
      <button>Migrate mebutton>
    Fragment>
  );
}
Enter fullscreen mode

Exit fullscreen mode

It’s a common pattern in React which is used for a component to return multiple elements. Fragments let you group a list of children without adding extra nodes to the DOM.

Why fragments are better than container divs?

  • Fragments are a bit faster and use less memory by not creating an extra DOM node. This only has a real benefit on very large and deep trees.
  • Some CSS mechanisms like Flexbox and CSS Grid have a special parent-child relationships and adding divs in the middle makes it hard to keep the desired layout.



Naming Conventions

Naming is a important part of writing clean and maintainable code and it’s required to take the time to choose descriptive and meaningful names for your functions and variables. When naming functions and variables, it’s important to choose names that are self explanatory and convey the purpose and function of the code.

For React, a component name should always be in a Pascal case like UserDashboard, Dashboard etc.. Using Pascal case for components differentiate it from default JSX element tags.

Methods/functions defined inside components should be in Camel case like getUserData(), showUserData() etc.

For globally used Constant fields in the application, try to use capital letters only. As an instance,

const PI = 3.14;
Enter fullscreen mode

Exit fullscreen mode




Remove Unnecessary Comments from the Code

Original code:

import React, { Fragment } from "react";

function Adventure() {
  console.log("test");

  return (
    <Fragment>
      {/* 

Adventure

*/
} <p>Break on Through To the Other Sidep> <button>Migrate mebutton> Fragment> ); }
Enter fullscreen mode

Exit fullscreen mode

Refactored code:

import React from "react";

function Adventure() {
  return (
    <>
      <p>Break on Through To the Other Sidep>
      <button>Migrate mebutton>
    
  );
}
Enter fullscreen mode

Exit fullscreen mode

Add comments only where it’s required so that you do not get confused while changing code at a later time.

Also don’t forget to remove statements like console.log, debugger, unused commented code.




Destructuring Props in React

Destructuring Props in React

Original code:

function Adventure(props) {
  return (
    <>
      <h1>Hello, {props.username}h1>
      <button>Migrate mebutton>
    
  );
}
Enter fullscreen mode

Exit fullscreen mode

Refactored code:

function Adventure({ username }) {
  return (
    <>
      <h1>Hello, {username}h1>
      <button>Migrate mebutton>
    
  );
}
Enter fullscreen mode

Exit fullscreen mode

Destructuring was introduced in ES6. This type of feature in the JavaScript function allows you to easily extract the form data and assign your variables from the object or array. Also, destructuring props make code cleaner and easier to read.




Respecting the Import Order

Original code:

import { formatCurrency, toNumber } from "@/utils";
import { useDrag } from "react-dnd";

import "./styleB.css";
import { useFormik } from "formik";
import React, { useEffect, useState } from "react";
import Button from "@/components/Button";
import TextField from "@/components/TextField";
import PropTypes from 'prop-types';
Enter fullscreen mode

Exit fullscreen mode

Refactored code:

import React, { useEffect, useState } from "react";
import PropTypes from 'prop-types';

import { useDrag } from "react-dnd";
import { useFormik } from "formik";

import { formatCurrency, toNumber } from "@/utils";

import Button from "@/components/Button";
import TextField from "@/components/TextField";

import "./styleB.css";
Enter fullscreen mode

Exit fullscreen mode

Organizing imports is a required part of writing clean and maintainable React code.

Import orders:

  1. React import
  2. Library imports (Alphabetical order)
  3. Absolute imports from the project (Alphabetical order)
  4. Relative imports (Alphabetical order)
  5. Import * as
  6. Import ‘./.

Each kind should be separated by an empty line. This makes your imports clean and easy to understand for all the components, 3rd-party libraries and etc.




Split your code into multiple smaller functions. Each with a single responsibility.

One of the key principles of writing clean and maintainable code is to keep functions and components small and focused. This means splitting your code into multiple smaller functions, each with a single responsibility.

When a function or component has too many responsibilities, it becomes harder to read, understand and maintain. By breaking it down into smaller, more focused functions, you can improve the readability and maintainability of your code.

To show you what I mean:
Split your code into multiple smaller functions




Don’t Repeat Yourself

Don’t Repeat Yourself (DRY) is a fundamental principle of software development. Which encourages developers to avoid duplicating code or logic throughout their codebase. The DRY principle is a required aspect of writing maintainable and scalable code and it applies to both coding standards and code refactoring.

In terms of coding standards, the DRY principle suggests that you should avoid duplicating code or logic in your codebase. This means that you should strive to write reusable code that can be used in multiple places in your application. As an instance, instead of writing the same validation logic for every input field in your application, you could write a reusable function that performs the validation and use it in each input field.


One way to improve the performance of your components is to avoid using arrow functions in the render() method.

What’s the problem with below instance?
Every time a component is rendered, a new instance of the function is created. Actually, it’s not a big deal in case the component is rendered one or two times. But in other cases, it can affect performance. Arrow functions in the render() method can cause unnecessary re renders of your components.

Original code:

function Adventure() {
  const [message, setMessage] = useState("Migration not started");

  return (
    <>
      <h1>Mesage: {message}h1>
      <button onClick={() => setMessage("Migration happened")}>
        Migrate me
      button>
    
  );
}
Enter fullscreen mode

Exit fullscreen mode

So if you care about performance, declare the function before using it in render as below instance:

Refactored code:

function Adventure() {
  const [message, setMessage] = useState("Migration not started");

  const onMessage = () => {
    setMessage("Migration happened");
  };

  return (
    <>
      <h1>Mesage: {message}h1>
      <button onClick={onMessage}>Migrate mebutton>
    
  );
}
Enter fullscreen mode

Exit fullscreen mode

Instead of using arrow functions in the render() method, you can declare methods as instance methods outside of the render() method. This ensures that the method is only created once and is not recreated on each render.




Decrease React Bundle Size

When you’re using third-party libraries in your React app, it’s very important to keep the bundle size as small as possible. A large bundle size can negatively impact the performance of your application, especially on slower internet connections.

One way to reduce the bundle size is to only load the parts of the library that you actually need. Instead of importing the entire library, you can import only the specific method that you need using a technique called tree shaking. This can significantly reduce the size of your bundle.

Let’s say you’re using the popular library Lodash and you only need the pick() method to pick certain properties from an object. Instead of importing the entire Lodash library, you can import only the pick() method as below:

import pick from 'lodash/pick';

const pickedUserProps = pick(userProps, ['name', 'email']);
Enter fullscreen mode

Exit fullscreen mode

instead of

import lodash form 'lodash';

const pickedUserProps = lodash.pick(userProps, ['name', 'email']);
Enter fullscreen mode

Exit fullscreen mode




Use useMemo to cache expensive calculations

When building a React app, it’s common to perform calculations or generate data that can be computationally expensive. This can negatively impact the performance of your app, especially if these calculations are performed frequently or in large amounts.

To kill the performance issues of your app, you can use the useMemo hook in React to cache expensive calculations. useMemo is a built in React hook that memoizes the result of a function, so that it is only recomputed when its dependencies have changed.

Let’s say you have a component that generates a list of items based on some data that is fetched from an API. The list is generated using a function that performs some expensive calculations:

function generateList(data) {
  // Perform some expensive calculations here
  // ...
  return list;
}

function Adventure() {
  const [data, setData] = useState([]);

  useEffect(() => {
    // Fetch data from API
    // ...
    setData(data);
  }, []);

  const list = generateList(data);

  return (
    <ul>
      {list.map((item) => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}
Enter fullscreen mode

Exit fullscreen mode

With the above instance, the generateList function is called every time the component re-renders, even if the data hasn’t changed. This can result in unnecessary computations and slow down the performance of your application.

To optimize this, you can use the useMemo hook to cache the result of generateList:

function Adventure() {
  const [data, setData] = useState([]);

  useEffect(() => {
    // Fetch data from API
    // ...
    setData(data);
  }, []);

  const list = useMemo(() => generateList(data), [data]);

  // ...
}
Enter fullscreen mode

Exit fullscreen mode




Remove too many if-else conditions

If-else statements are a common feature in most programming languages and they are often used to control the flow of code execution based on certain conditions. Though, too many if-else statements can make your code difficult to read, maintain and debug..

One approach to removing too many if-else conditions is to use the “switch” statement. The switch statement is similar to an if-else statement, but it is designed to handle multiple cases in a more concise and efficient way. Let’s see an instance:

function checkGrade(grade) {
  if (grade >= 90) {
    return "A";
  } else if (grade >= 80) {
    return "B";
  } else if (grade >= 70) {
    return "C";
  } else if (grade >= 60) {
    return "D";
  } else {
    return "F";
  }
}
Enter fullscreen mode

Exit fullscreen mode

In above instance, we are using an if-else statement to check the grade of a student and return a letter grade. This code can be refactored using the switch statement:

function checkGrade(grade) {
  switch (true) {
    case (grade >= 90):
      return "A";
    case (grade >= 80):
      return "B";
    case (grade >= 70):
      return "C";
    case (grade >= 60):
      return "D";
    default:
      return "F";
  }
}
Enter fullscreen mode

Exit fullscreen mode

As you can see, the switch statement is more concise and easier to read than the if-else statement. It also allows us to handle multiple cases in a more efficient way.

Another approach to removing too many if-else conditions is to use object literals. Object literals are a way of defining key and value pairs in JavaScript.

To show you what I mean:

function getAnimalSound(animal) {
  if (animal === "dog") {
    return "woof";
  } else if (animal === "cat") {
    return "meow";
  } else if (animal === "cow") {
    return "moo";
  } else {
    return "unknown";
  }
}
Enter fullscreen mode

Exit fullscreen mode

In above instance, we are using an if-else statement to return the sound of an animal. This code can be refactored using object literals:

function getAnimalSound(animal) {
  const animalSounds = {
    dog: "woof",
    cat: "meow",
    cow: "moo"
  }
  return animalSounds[animal] || "unknown";
}
Enter fullscreen mode

Exit fullscreen mode

As you can see, the object literals approach is more concise and easier to read than the if-else and switch statement. It also allows us to define key and value pairs in a more flexible way..




End of an adventure

Code refactoring is all about breaking through the limitations of your current code and pushing towards a better, more efficient and effective version. It can be a daunting task, but with the right mindset, tools and best practices, you can take your code to the Other Side.

Code refactoring is not just about improving the quality of your code, but also about improving the overall user experience of your app. By putting in the effort to refactor your code, you can create a more responsive, intuitive and engaging app that users will love.

So, don’t be afraid to break through to the other side. Hug the process of code refactoring and watch as your app rises to new heights..




Motivation

Motivation




🍀Support

Please consider following and supporting us by subscribing to our channel. Your support is greatly appreciated and will help us continue creating content for you to enjoy. Thank you in advance for your support!

YouTube
GitHub
Twitter

Thank you in advance for your support

Total
0
Shares
Valentsea

Top 5+ Free Tailwind CSS React Admin Dashboards & Templates for 2023

Tailwind CSS has been gaining popularity in the web development community due to its easy-to-use utility classes and…

You May Also Like