Categories
Refactoring

Cleaning Up Our JavaScript Code by Refactoring Them — Encapsulation

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to write a piece of clean JavaScript code.

In this article, we’ll look at ways to refactor our code by encapsulating our API calls in their own React component, and use factory functions to provide polymorphism in our JavaScript code.

Encapsulate APIs in Our React Components

To decouple API calls from our components, we can call one API per component.

This way, if one API changes, it won’t break other components. It also keeps components simple because at most they use one API.

For instance, we can create components and use them as follows:

import React, { useState, useEffect } from "react";

const Person = () => {
  const [person, setPerson] = useState({});
  const getData = async () => {
    const res = await fetch("https://api.agify.io?name=michael");
    const p = await res.json();
    setPerson(p);
  };
  useEffect(() => {
    getData();
  }, []);

  return <p>{person.name}</p>;
};

const Dog = () => {
  const [dog, setDog] = useState({});
  const getData = async () => {
    const res = await fetch("https://dog.ceo/api/breeds/image/random");
    const d = await res.json();
    setDog(d);
  };
  useEffect(() => {
    getData();
  }, []);

  return <img src={dog.message} alt="" />;
};

export default function App() {
  return (
    <div className="App">
      <Person />
      <Dog />
    </div>
  );
}

In the code above, we have 2 components, each of which calls a single. The Person component calls one and Dog calls another. They only render something simple.

Then in App , we use them both at the same level. This way, we can combine them into one place without creating one large component.

If we have to share data between these components, then we can use some state management solutions like Redux or Mobx to share data in a centralized place in a way that’s decoupled from the component.

Replace Conditional with Polymorphism

Polymorphism is a provision of creating a single interface of entities to different types.

In JavaScript, there’re no interfaces, but we can still use this principle to let us create functions that returns objects of different class instances that have the same methods that we can call by calling one function instead of instantiating multiple classes ourselves and calling the methods ourselves.

For instance, we can create a factory function that instantiates multiple classes and return one by checking the arguments that we pass into the factory function.

Instead of writing:

const meow = (animal) => {
  //...
};
const bark = (animal) => {
  //...
};
if (animal === 'cat') {
  meow(animal);
}

if (animal === 'dog') {
  bark(animal);
}

We can instead create classes and instantiate them with a factory function as follows:

class Cat {
  speak() {}
}

class Dog {
  speak() {}
}

const Animal = (type) => {
  if (type === 'cat') {
    return new Cat();
  } else if (type === 'dog') {
    return new Dog();
  }
}

const cat = Animal('cat');
const dog = Animal('dog');
cat.speak();
dog.speak();

In the code above, we have 2 classes, Cat and Dog . They have the same speak method instead of the meow and bark functions.

Then we created an Animal class, which has the type parameter to let us specify the type of animal to instantiate. If the type is 'cat' , then we return a new Cat instance. If the type is 'Dog' , then we return a new Dog instance.

After that, we can use the same Animal function to create these 2 class instances and call the speak method on the returned object.

This way, we only have to depend on the Animal function rather than a bunch of different functions. So we don’t have to worry about anything other than Animal having breaking changes.

This is a commonly used principle to reduce coupling. We reduce the number of dependencies that we have to rely on to reduce coupling so that we have fewer chances of breaking our code when anything changes.

This makes our code more robust.

Conclusion

To keep our React components simple, we should break them up so that they only do one thing. One way to do that is to only have each component call a maximum of one API.

Polymorphism is also a good way to reduce coupling. We can create a factory function that instantiates classes for us and have the class include the same method names so that they implement one interface and we don’t have to worry about the implementation of those classes.

Even though JavaScript has no interfaces, we can still make sure that classes have the same methods included.

Categories
Refactoring

JavaScript Refactoring Tips — Making Functions Clearer and Cleaner

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to write a piece of clean JavaScript code.

In this article, we’ll look at how to make our functions clearer.

Using Destructuring for Object Parameters

If we want our function to take lots of arguments, then we should take one big object argument.

To do that, we should use the destructuring syntax to destructure the properties of the object we pass in as the argument as variables.

For instance, instead of writing:

const greet = (obj) => {
  return `${obj.greeting}, ${obj.firstName}${obj.lastName}`;
}

We should write:

const greet = ({
  greeting,
  firstName,
  lastName
}) => {
  return `${greeting}, ${firstName}${lastName}`;
}

This way, we have a lot less repetition, and it makes everyone clear what properties the object has in the arguments.

It’s like having multiple arguments without actually having to pass in multiple arguments.

It’s a lot more flexible as the order of properties in an object doesn’t matter as with multiple parameters, but we still can access each property individually as variables.

Naming Our Callback Functions

Naming things makes reading code easier. This is the same with callback functions. For instance, instead of writing:

const arr = [1, 2, 3].map(a => a * 2);

We should instead write:

const double = a => a * 2;
const arr = [1, 2, 3].map(double);

Now we know that our callback function actually is used for doubling each entry of the original array.

Make Our Conditionals Descriptive

Conditionals can be made more descriptive by writing the conditional expressions in a conditional statement in their own function.

For instance, instead of writing:

if (score === 100 ||
  remainingPlayers === 1 ||
  remainingPlayers === 0) {
  quitGame();
}

We should write:

const winnerExists = () => {
  return score === 100 ||
    remainingPlayers === 1 ||
    remainingPlayers === 0
}

if (winnerExists()) {
  quitGame();
}

This way, we know that those conditions are conditions for checking if a winner exists in our game code.

In the first example, we have a long-expression inside the parentheses of the if that most people probably don’t know that it’s checking.

But in the 2nd example, once we put it in a named function, we know that the conditions are actually checking for.

Having a named function in our conditionals is much clearer than just having a bunch of boolean expressions together.

Replacing Switch Statements with Maps or Objects

switch statements are long and error-prone because of its length. Therefore, we should replace them with shorter pieces of code if we can.

Many switch statements can be replaced with maps or objects. For instance, if we have the following switch statement:

const getValue = (prop) => {
  switch (prop) {
    case 'a': {
      return 1;
    }
    case 'b': {
      return 2;
    }
    case 'c': {
      return 3;
    }
  }
}
const val = getValue('a');

We can replace that with an object or map as follows:

const obj = {
  a: 1,
  b: 2,
  c: 3
}
const val = obj['a'];

As we can see, the switch was long. We need to nest multiple blocks with multiple return statements just to get a value returned given a prop value.

With an object, we have an object obj :

const obj = {
  a: 1,
  b: 2,
  c: 3
}

which has 3 properties with string keys that we can return a value from if we access it with the bracket notation as we did above. With the bracket notation, the keys don’t have to be valid identifiers. Instead, they can be any string.

We can also replace the object with a map as follows:

const map = new Map([['a', 1], ['b', 2], ['c', 3]])
const val = map.get('a')

As we can see, with maps, the code is also a lot shorter. We defined the Map with one line by passing in an array with entries that consists of arrays of key and value in that order.

Then we just use the Map instance’s get method to get the value from the key.

One benefit that maps have over objects is that we can have other values like numbers, booleans, or objects as keys. Whereas objects can only have strings or symbols as keys.

Also, we don’t have to worry about inherited properties with maps.

Conclusion

Object parameters can be made clearer and shorter with the destructuring syntax. This way, the properties can be selectively accessed as variables.

Conditionals can be made more descriptive by putting the conditional expressions in its own named function. Likewise, we should name our callback functions to make reading the code easier.

Finally, switch statements should be replaced with maps and objects as much as possible.

Categories
Refactoring

JavaScript Refactoring Tips — Reducing Function Complexity

JavaScript is an easy to learn programming language. It’s easy to write programs that run. However, it’s hard to write a piece of clean JavaScript code.

In this article, we’ll look at ways to reduce the complexity of our JavaScript functions.

Move Repeated Code into a Single Location

We should move repeated code into a single location so that we can change them easily if needed as we only need to change one instead of multiple pieces of code.

For instance, instead of writing:

const button = document.querySelector('button');
let toggled = true;
button.addEventListener('click', () => {
  toggled = !toggled;
  if (toggled) {
    document.querySelector("p").style.color = 'red';
  } else {
    document.querySelector("p").style.color = 'green';
  }
})

where we have 2 instances of document.querySelector(“p”) , we should write:

const button = document.querySelector('button');
const p = document.querySelector("p");
let toggled = true;
button.addEventListener('click', () => {
  toggled = !toggled;
  if (toggled) {
    p.style.color = 'red';
  } else {
    p.style.color = 'green';
  }
})

As we can see, it’s shorter and we only have to change the selector once if we decide that we don’t want to select the p element anymore.

Another common instance of code is magic numbers, where we have code with numbers that we don’t know the meaning of from the code itself.

For instance, we have code that looks like:

let x = 1;
let y = 1;
let z = 1;

Where we have 3 1’s that we don’t know the meaning of. We can remove the repetition and make the numbers self-documenting by writing:

const numPeople = 1;
let x = numPeople;
let y = numPeople;
let z = numPeople;

Then we know that 1 is the value of numPeople . Now we know that 1 means the number of people.

Simplify Functions

Functions should be as simple as possible. Therefore, they should only do one thing and not have too many lines of code in them. They shouldn’t have more than 30 lines of code.

The old cases that we use JavaScript functions shouldn’t be used. For instance, we shouldn’t use IIFEs for modules or blocks. Also, we shouldn’t define constructor functions anymore.

Instead, we should use the class syntax, where we can include multiple instance methods of the class inside the class.

This reduces the need for long functions significantly.

Also, functions should be pure whenever we can define them. This means they shouldn’t commit side effects.

For instance, the best simple functions are functions like:

const add = (a, b) => a + b;

The function above doesn’t have any side effects, as it doesn’t modify any variables outside the function. Also, it’s pure because it always returns the same outputs for the same inputs.

It also only does one thing, which is adding numbers. Therefore, the cognitive load on the reader of the code isn’t going to be too taxed since it only does one thing and nothing else.

Use Guard Clauses Instead of Nested If Statements

Guard clauses are where we end the function when the function encounters some condition.

It replaces nested if blocks that are long and hard to read.

For instance, instead of writing the following function:

const greet = (firstName, lastName, greeting) => {
  if (typeof firstName === 'string') {
    if (typeof lastName === 'string') {
      if (typeof greeting === 'string') {
        return `${greeting}, ${firstName}${lastName}`;
      }
    }
  }
}

We write:

const greet = (firstName, lastName, greeting) => {
  if (typeof firstName !== 'string') {
    throw new Error('first name is required');
  }
  if (typeof lastName !== 'string') {
    throw new Error('last name is required');
  }
  if (typeof greeting !== 'string') {
    throw new Error('greeting is required');
  }
  return `${greeting}, ${firstName}${lastName}`;
}

In the 2nd example, we eliminated the nested if statements by throwing errors if each argument isn’t a string.

This reduced the nested if statements to no nested if statements while doing the same thing.

Nesting is hard to read and understand, and we should get rid of them everywhere.

Conclusion

Repeated code is always bad. We should always remember the Do not Repeat Yourself (DRY) principle.

Also, a lot of things that previously used traditional functions for shouldn’t be used anymore. Many functions can be replaced with modules, blocks, classes, and arrow functions.

Finally, nested if statements should be replaced with guard clauses since they can do the same checks as the nested if statements without having hard to read nested if statements.

Categories
Refactoring

JavaScript Refactoring Tips — Classes and Functions

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to write a piece of clean JavaScript code.

In this article, we’ll look at how to refactor our JavaScript functions so that it’s clean and easy to read.

Refactor Constructors to Classes

In JavaScript, a class is just syntactic sugar for constructor functions, so they’re actually functions.

All classes do is provide a more intuitive syntax for constructor functions by avoiding dealing with prototypes and using call to call parent constructor functions when we extend another constructor.

For instance, instead of writing:

function Foo(name) {
  this.name = name;
}

Foo.prototype.getName = function() {
  return this.name;
}

We write:

class Foo {
  constructor(name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }
}

As we can see, with the class syntax, it’s clear where the constructor is and what instance methods our class contain with the class syntax.

Instead of attaching instance methods to the prototype of the constructor function, we add it to the class.

We also don’t have to write out the keyword function all the time, which makes typing shorter while keeping the code easy to understand. There’s no ambiguity with this syntax.

If we want to do inheritance, instead of writing:

function Animal(name) {
  this.name = name;
}
Animal.prototype.getName = function() {
  return this.name;
}

function Cat(name) {
  Animal.call(this, name);
}
Cat.prototype.constructor = Animal;

We write:

class Animal {
  constructor(name) {
    this.name = name;
  }
  getName() {
    return this.name;
  }
}

class Cat extends Animal {
  constructor(name) {
    super(name);
  }
}

This way, we get an error is we forgot to call the parent’s constructor, which we don’t get when we forgot to call the parent constructor in the constructor function example.

We also make it clear that we’re extending the Animal class with the extends keyword instead of having to set the parent constructor in the child constructor ourselves.

Avoiding Traditional Functions As Much As Possible

Traditional functions were used for everything before ES2015. It’s used for encapsulating code, used as blocks, used for constructors as we have above, used for callbacks, etc.

Most of these cases have been replaced by other constructors that were introduced with ES2015.

If we want to encapsulate code, we can use blocks. For instance, instead of writing an immediately invoked function expression (IIFE) as follows:

(function() {
  let x = 1;
  console.log(x);
})()

We can instead write:

{
  let x = 1;
  console.log(x);
}

The 2nd example is shorter and we only have to delimit the block with the curly braces.

The variable x isn’t available outside the block with the code above.

Another way to encapsulate code is to use modules. For instance, we can write the following code:

module.js

export const x = 1;
const y = 2;
const z = 3;
export default y;

index.js

import { x } from "./module";
import y from "./module";
console.log(x, y);

In the code above, we only expose what we have when exporting them with the export keyword. Therefore, x and y are available for import from another module.

As we can see, x and y were imported from module.js . But z couldn’t be because it wasn’t exported.

Therefore, we don’t need IIFEs like the following anymore:

const module = (function() {
  const x = 1;
  const y = 2;
  const z = 3;
  return {
    x,
    y
  }
})();

The code above is longer and it uses a function unnecessarily. Also, as the function has more members, it’ll get longer, and it isn’t a good idea to have long functions.

For callbacks, we can use arrow functions instead. For instance, instead of writing:

const arr = [1, 2, 3].map(function(a) {
  return a * 2;
})

We should write:

const arr = [1, 2, 3].map(a => a * 2)

It’s shorter, so we don’t have to type as much as we don’t have to type our the function keyword. Also, the return is implicit for one lined arrow functions. And we also don’t have to worry about the value of this and arguments since arrow functions don’t bind to any of these values.

Conclusion

To refactor functions, we should convert constructors to classes. Otherwise, we should convert them to blocks, modules, and arrow functions as we see fit.

Categories
Koa Nodejs

Validating Request Data with Koa

Koa is a small framework that lets us create backend apps that run on the Node.js platform.

In this article, we’ll look at how to validate request data type with Koa.

Validating Request Data Type Using the request.is Method

We can call the ctx.is method with the string for the data type that we’re checking for to check if the data type from the Content-Type header’s value matches what we have in the argument.

It can take one or more data types. For instance, we can use it as follows:

const Koa = require('koa');
const app = new Koa();

app.use(async (ctx, next) => {
  if (ctx.is('text/*', 'text/html')){
    ctx.body = 'valid';
  }
  else {
    ctx.body = 'invalid';
  }
});

app.listen(3000);

In the code above, we called the ctx.is method with the strings for 2 data types, which are ‘text/*’ and ‘text/html’ to check if the incoming request have any of these values as the value of its Content-Type header.

If one of these values are includes, then it returns 'valid' as the response body.

Otherwise, it returns 'invalid' .

Content Negotiation

Content Negotiation is a mechanism that’s used to serve different representations of a resource from the same URL. We can use the user agent that can specify which is best suited for the user.

When making requests to the client, we can use the Accept , Accept-Charset , Accept-Encoding , or Accept-Language headers in the request to let the server know what kind of data the client accepts.

With that data, the server can return a response with the most suitable kind of data for the client.

For instance, we can use it as follows:

const Koa = require('koa');
const app = new Koa();

app.use(async (ctx, next) => {
  if (ctx.accepts('html')){
    ctx.body = 'accepts html';
  }
  else if (ctx.accepts('application/json')){
    ctx.body = 'accepts json';
  }
  else if (ctx.accepts('image/png')){
    ctx.body = 'accepts png';
  }
  else {
    ctx.body = 'accepts everything';
  }
});

app.listen(3000);

In the code above, we called the ctx.accepts method with the content-type string that we want to check for in those headers listed above, so that we can return the appropriate response for it.

Then when we make a request to our app with one of those headers and application/json as its value, then we get the string 'accepts json' returned as the response.

Checking for the Encoding that the Client Accepts

We can use the ctx.acceptsEncodings method to check what kind of encoding the client accepts.

It takes one more encoding type strings as its argument or it can be one array of encoding type strings.

For instance, we can use it as follows:

const Koa = require('koa');
const app = new Koa();

app.use(async (ctx, next) => {
  if (ctx.acceptsEncodings('gzip') === "gzip"){
    ctx.body = 'accepts gzip';
  }
  else {
    ctx.body = 'does not accept gzip';
  }

});

app.listen(3000);

In the code above, we called the ctx.acceptEncodings method with the argument 'gzip' . The method returns the encoding that’s the best match.

Then if we make a request to our app with the Accept-Encoding header gzip, deflate, br , we’ll get that the response 'accepts gzip' from the app.

If no arguments are given, then all accepted encodings are returned.

Checking the Character Set That’s Accepted By the Client

We can call the acceptsCharsets to check for the character sets that are accepted by the client.

It also takes one or more string for the character set names that are accepted or an array of strings with those character set names.

For instance, we can use it as follows:

const Koa = require('koa');
const app = new Koa();

app.use(async (ctx, next) => {

  if (ctx.acceptsCharsets('utf-8') === 'utf-8'){
    ctx.body = 'accepts utf-8';
  }
  else {
    ctx.body = 'does not accept utf-8';
  }

});

app.listen(3000);

The code above is similar to the acceptEncodings code.

If we make a request that doesn’t include the Accept-Charset header that includes utf-8 , then we’ll get ‘does not accept utf-8’ returned as the response.

Otherwise, we’ll get 'accepts utf-8' returned whether we made the request explicitly with the Accept-Charset header with utf-8 as the value or not.

Checking the Language of the Request

The ctx.acceptsLanguages let us check the language of the request according to the Accept-Language header.

It’s similar to the other 2 methods above with the arguments that it takes and what it returns.

Get Any Header Field

We can get any request header field with the ctx.get method. For instance, we can write the following to do that:

const Koa = require('koa');
const app = new Koa();

app.use(async (ctx, next) => {
  ctx.body = ctx.get('foo');
});

app.listen(3000);

In the code above, we called the get method with the 'foo' header name. It returns the value of the 'foo' header if it exists.

Then when we make a request with the foo header with value bar , we’ll see 'bar' returned as the response.

Conclusion

We can use the ctx.is method to validate data types that are sent to the server.

To do content negotiation, we can use the accepts method. To check the language, encoding, or character set, there’re methods to do that.

Finally, we can get any request header value with the get method.