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.