Skip to content

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Importing Utility Functions in React.js

As your React.js app grows, logical routines are extracted from your code as utility functions and placed in another file so other components can use them.

Sep 24, 2020 • 9 Minute Read

Introduction

As your React.js app grows, you might identify certain code smells in your code, such as repetition. Normally these logical routines are extracted from your code as utility functions to be placed in a separate file so other components can utilize them. Examples of this would be formatting numbers or performing various computations. In this guide, you will create a toy app that applies these concepts by separating functions into separate files and importing them from your component. The app takes in the input of BTC (a type of cryptocurrency) and the BTC rate to the dollar and displays the dollars you would have based on those inputs.

Setup

Start off by creating the component called BTCDollarConverter which looks like the following:

      import React from 'react';

export default class BTCDollarConverter extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      btc: 0.00,
      btcRate: 0.00,
      dollars: 0.00
    }
  }

  handleBtcChanged(event) {
    var btc     = parseFloat(event.target.value);
    var dollars = btc * this.state.btcRate;

    this.setState({
      btc: btc,
      dollars: dollars
    });
  }

  handleBtcRateChanged(event) {
    var btcRate = parseFloat(event.target.value);
    var dollars = this.state.btc * btcRate;

    this.setState({
      btcRate: btcRate,
      dollars: dollars
    });
  }

  render() {
    return (
      <div>
        BTC: 
        <input
          type="number"
          value={this.state.btc}
          onChange={this.handleBtcChanged.bind(this)}
        /> 
        at 
        <input
          type="number"
          value={this.state.btcRate}
          onChange={this.handleBtcRateChanged.bind(this)}
        /> 
        Dollars per BTC = {this.state.dollars}
      </div>
    );
  }
}
    

This component maintains three states:

  1. btc: The amount of BTC you have
  2. btcRate: The rate of US dollars per BTC
  3. dollars: How many dollars you will have given the amount of btc you have times btcRate

The first input, which controls the amount of BTC you have, is given by the following under the render() method of the component:

      <input
  type="number"
  value={this.state.btc}
  onChange={this.handleBtcChanged.bind(this)}
/>
    

As you can see, this input element is bound to the state btc, and every time the user changes the value it will trigger this.handleBtcChanged(event) as bound by the onChange attribute:

      handleBtcChanged(event) {
  var btc     = parseFloat(event.target.value);
  var dollars = btc * this.state.btcRate;

  this.setState({
    btc: btc,
    dollars: dollars
  });
}
    

The logic is pretty straightforward. It first takes the value of the input and stores it in a temporary variable btc. It multiplies that value with the current btcRate state value to give the amount of dollars. It then invokes setState() to update the state values for both btc and dollars forcing the component to re-render with the updated values.

The same goes for btcRate's input:

      <input
  type="number"
  value={this.state.btcRate}
  onChange={this.handleBtcRateChanged.bind(this)}
/>
    

Its value is bound to the state value btcRate. Every time the user modifies the input element, it invokes handleBtcRateChanged(event):

      handleBtcRateChanged(event) {
  var btcRate = parseFloat(event.target.value);
  var dollars = this.state.btc * btcRate;

  this.setState({
    btcRate: btcRate,
    dollars: dollars
  });
}
    

This method takes the input and stores it in a temporary variable btcRate, which is then multiplied to the current state of this.state.btc to give the final amount in dollars. Both btcRate and dollars are then updated by setState().

Creating Utility Functions

Notice that at each change of the input, whether it be btc or btcRate, the dollars variable is recomputed and displayed automatically. There are two problems with this approach:

  1. The computation is the same for both handleBtcChanged() and handleBtcRateChanged.
  2. The dollars value is not formatted and will display as many decimal places as are computed.

To solve these problems, you'll create two separate functions in a separate file that the component will import. You could always have these functions within the component, but in practice such utility functions should be placed in their own respective file for reusability.

Computational Function

Since the computation is just a multiplication of two values, the function to address the first problem will look like the following:

      function computeDollars(btc, btcRate) {
  return btc * btcRate;
}
    

Formatting Function

Suppose that the rule for formatting dollars is that for every three digits to the left of the decimal place you put a "," and that the value should be rounded to two decimal places. The function would look like this:

      function formatDollars(dollars) {
  dollars = (Math.round(dollars * 100) / 100).toFixed(2);
  dollars = dollars.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");

  return dollars;
}
    

The first line in the function rounds the value to two decimal places. The second line uses a regular expression as the first argument of replace() that looks for the position of every group of three integers, while the second argument is the character that would be inserted in that position.

Using an External File

Put both functions in an external JavaScript file called utils.js within the same directory as the component. Since the functions will be exported as modules, you need to prepend it with the export keyword. utils.js will look like this:

      export function computeDollars(btc, btcRate) {
  return btc * btcRate;
}

export function formatDollars(dollars) {
  dollars = (Math.round(dollars * 100) / 100).toFixed(2);
  dollars = dollars.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");

  return dollars;
}
    

Over at your BTCDollarConverter component, add the following at the top part of the file (after import React from 'react') to import the two functions:

      import { computeDollars, formatDollars } from './utils.js';
    

You can now call the utility functions within the component. handleBtcChanged(event) and handleBtcRateChanged(event) will now be:

      handleBtcChanged(event) {
  var btc     = parseFloat(event.target.value);
  var dollars = computeDollars(btc, tihs.state.btcRate);

  this.setState({
    btc: btc,
    dollars: dollars
  });
}

handleBtcRateChanged(event) {
  var btcRate = parseFloat(event.target.value);
  var dollars = computeDollars(this.state.btc, btcRate);

  this.setState({
    btcRate: btcRate,
    dollars: dollars
  });
}
    

And within the render() method, displaying dollars will now look like this:

      Dollars per BTC = {formatDollars(this.state.dollars)}
    

Overall Code

utils.js will contain:

      export function computeDollars(btc, btcRate) {
  return btc * btcRate;
}

export function formatDollars(dollars) {
  dollars = (Math.round(dollars * 100) / 100).toFixed(2);
  dollars = dollars.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");

  return dollars;
}
    

The final form of the component will look like this:

      import React from 'react';
import { computeDollars, formatDollars } from './utils.js';

export default class BTCDollarConverter extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      btc: 0.00,
      btcRate: 0.00,
      dollars: 0.00
    }
  }

  handleBtcChanged(event) {
    var btc     = parseFloat(event.target.value);
    var dollars = computeDollars(btc, this.state.btcRate);

    this.setState({
      btc: btc,
      dollars: dollars
    });
  }

  handleBtcRateChanged(event) {
    var btcRate = parseFloat(event.target.value);
    var dollars = computeDollars(this.state.btc, btcRate);

    this.setState({
      btcRate: btcRate,
      dollars: dollars
    });
  }

  render() {
    return (
      <div>
        BTC: 
        <input
          type="number"
          value={this.state.btc}
          onChange={this.handleBtcChanged.bind(this)}
        /> 
        at 
        <input
          type="number"
          value={this.state.btcRate}
          onChange={this.handleBtcRateChanged.bind(this)}
        /> 
        Dollars per BTC = {formatDollars(this.state.dollars)}
      </div>
    );
  }
}
    

Now, as the user modifies the values in both input elements, the component will recompute the value for dollars and render it in the proper comma delimited format. Try it out yourself!

Conclusion

This component can dynamically change the computed value of a state variable. The computation and formatting of the state variable is extracted to its own respective functions in a separate file. The original component can then import these functions and use it accordingly. This way, you as the developer can now separate logic that is reusable across multiple components following the DRY principle (Don't Repeat Yourself).

Raphael Alampay

Raphael A.

Raphael Alampay is the co-founder of Cloudband Solutions Co., a software development and consultancy company that caters to custom based software for SME around the globe. Using time and tested technology such as Java, Ruby on Rails, Python, PostgreSQL and Linux, he has a passion for creating applications that solve real world problems making businesses more efficient and innovative at the same time. His craft in software is largely based on the philosophy of "kaizen" which means continuous improvement; that any piece of software is meant to be continuously improved over time. Aside from software development, Raphael also teaches computer science in universities and corporate entities largely in the field of programming and machine learning. He currently holds a master's degree in Computer Science and is working on his PhD in Computer Science as well. If not programming or teaching, Raphael enjoys playing the piano and guitar in his spare time.

More about this author