Author avatar

Raphael Alampay

Importing Utility Functions in React.js

Raphael Alampay

  • Sep 24, 2020
  • 9 Min read
  • 188 Views
  • Sep 24, 2020
  • 9 Min read
  • 188 Views
Web Development
Front End Web Development
Client-side Frameworks
React

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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>
    );
  }
}
javascript

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:

1
2
3
4
5
<input
  type="number"
  value={this.state.btc}
  onChange={this.handleBtcChanged.bind(this)}
/> 
jsx

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:

1
2
3
4
5
6
7
8
9
handleBtcChanged(event) {
  var btc     = parseFloat(event.target.value);
  var dollars = btc * this.state.btcRate;

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

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:

1
2
3
4
5
<input
  type="number"
  value={this.state.btcRate}
  onChange={this.handleBtcRateChanged.bind(this)}
/> 
jsx

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

1
2
3
4
5
6
7
8
9
handleBtcRateChanged(event) {
  var btcRate = parseFloat(event.target.value);
  var dollars = this.state.btc * btcRate;

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

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:

1
2
3
function computeDollars(btc, btcRate) {
  return btc * btcRate;
}
javascript

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:

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

  return dollars;
}
javascript

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:

1
2
3
4
5
6
7
8
9
10
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;
}
javascript

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:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
  });
}
javascript

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

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

Overall Code

utils.js will contain:

1
2
3
4
5
6
7
8
9
10
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;
}
javascript

The final form of the component will look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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>
    );
  }
}
javascript

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

1