Front End Interview Question
Luc van Kerkvoort
Posted on July 14, 2022
We as programmers lead a hard life. Especially when it comes to the interview process. There are many obstacles we have to face to get a new job, which is never fun to do.
I bet all of you reading this are excellent programmers, but a lot of us have a problem when we get put on the spot... performing under pressure is not our forte. This is why we didn't become olympic swimmers, or deep sea divers...
All jokes aside I think it is important for us to document the hoops we have to jump through to get a job. That is why I wrote this article about a take home assignment I once received from a company.
The Assignment
I received the following requirements
- Create a button that once clicked showcases the current price of Bitcoin
- After the button is clicked the buttons text changes from "get Bitcoin price" to "refresh Bitcoin price"
- After the button is clicked it changes color from blue to purple
- Once the button is clicked a field shows up with "Current price: " after a consecutive click a second field shows up with "Previous price: "
This was the entirety of the brief, the assignment had little to no restrictions to it. You can use libraries however you like them. You can use outside sources to fill in the code and you have 1 hour to complete it.
The Approach
I started thinking about how I wanted to structure the code keeping in mind best practices and going for a functional programming solution. In functional programming we break big problems down into modular solutions. This means we build functions that are reusable and pure
Logic
So I started dividing up the work and I came up with the following approach and pseudo code for the logic:
Button press
I'll use a useState to keep track of the button state.
This is also a value we can pass along to the button to change the color of it and use it to set the text for it
// Button press
const [isPressed, setPressed] = useState(false)
API call
I'll create a modular API call for the bitcoin call with a try catch statement which will return an empty array so the page will still load even if the call fails.
// API call
const getBitcoin = async() => {
try{
fetch(url)
} catch {
return []
}
}
Setting Amount
thinking about this we need a data structure to keep track of the bitcoin prices. In my mind the best approach would be a Queue where a price comes in and we display it as current, then a second price comes in and current becomes the new price and the old price becomes the previous price and so on and so forth
// setting amount
const [bitcoinPrices, setPrice] = useState([])
const setAmount = (data = {}) => {
const newPrices = [data, ...bitcoinPrices].slice(2)
setPrice(newPrices)
}
These will guide me into creating the application.
User Interface
Now We'll have to build out the UI for this application. Let me add that the UI looks kind of funky because there were no restrictions to what the UI looks like, this was purely a showcase of logic skills
<div className="App">
<button>
{isPressed ? "refresh Bitcoin price" : "get Bitcoin price"}
</button>
<div>
<div>
{bitcoinPrice.map((value, i) => (
<div key={i}>
{i === 0 ? "Current price: " : "Previous price: "} {value}
</div>
))}
</div>
</div>
</div>
as you can see I put in a ternary changing the text of the button from "get Bitcoin price" to "refresh Bitcoin price".
I am mapping the bitcoinPrice (state variable) to set the value. I'm using a ternary to decide the text in front of the value. If it is the index of 0 we set it to "Current Price" otherwise we set it to "Previous Price"
Extra curricular points
In this challenge I had to build it on my own machine while the interviewer was watching. I created a new React project using npx create-react-app
. I got prompted the question why I used create-react-app
.
These kind of questions are a great tool to share your knowledge. It is extra curricular points to know the answers to these kind of questions, so if you don't that is okay but here is an acceptable answer to the question
create-react-app is a package that presets an entire React application which installs babel
which is a compiler used to parse JSX code to plain ES5 JavaScript, it installs webpack
a bundler and presets it to bundle the application, installs React
and presets the Virtual DOM, sets up testing with React testing library
, so right out of the box we can start coding away.
The other good thing about it is that you can install and talk at the same time. Hopefully by the time you are done talking the installation will be complete.
The Execution
Under the watchful eye from my interviewer I started building out the application. first a draft version to get things started:
function App() {
const [isPressed, setPressed] = useState(false)
const [bitcoinPrice, setBitcoinPrice] = useState([])
const getBitcoin = async() => {
try {
const call = await fetch("https://api.coinbase.com/v2/prices/BTC-USD/buy", {
method: "GET",
authorization:
"Bearer abd90df5f27a7b170cd775abf89d632b350b7c1c9d53e08b340cd9832ce52c2c",
});
const response = await call.json();
return await response;
} catch {
return [];
}
};
const setAmount = () => {
const data = await getBitcoin()
if(!data) return
console.log(data)
// {"data":{"base":"BTC","currency":"USD","amount":"19891.09"}}
const {data: { amount }} = data
// New amount is pushed in front of the old amounts that have been deconstructed.
// We then slice the array at 2 so we get rid of any old data we don't want to use anymore
const newPrices = [amount, ...bitcoinPrice].slice(2)
setBitcoinPrice(newPrices)
}
return(
<div className="App">
<button onClick={() => {
if(!isPressed) setPressed(true)
setAmount()
>
{isPressed ? "refresh Bitcoin price" : "get Bitcoin price"}
</button>
<div>
<div>
{bitcoinPrice.map((value, i) => (
<div key={i}>
{i === 0 ? "Current price: " : "Previous price: "} {value}
</div>
))}
</div>
</div>
</div>
)
}
export default App
Here I build the draft for the application. I felt like it is a lot of code on a single page but it was executing everything I needed for the logic, however on the User Interfaces the button wasn't changing color yet, so we had to tackle that with a library.
I asked the interviewer if I'm allowed to install libraries. He said yes, but you would have to explain the use case of the library.
I decided I wanted to implement styled-components
, as a CSS library that allows me to send and use JavaScript right into my CSS. This is extremely handy if you have to set colors on elements using a ternary operator.
Styles
I created the following css file to style the Button
and implemented the ternary operator to change the background color to purple after the button has been pressed, I also found that the list items were horizontally aligning to each other so I decided to use flexbox
to fix that issue as you can see in the List
component
import styled from "styled-components";
export const Button = styled.button`
background-color: ${({ isPressed }) => (isPressed ? "blue" : "purple")};
`;
export const List = styled.div`
display: flex;
flex-direction: column;
`;
with that out of the way I imported the components into the main file and I implemented the new styled components as shown below:
return(
<div className="App">
<Button isPressed={isPressed} onClick={() => {
if(!isPressed) setPressed(true)
setAmount()
>
{isPressed ? "refresh Bitcoin price" : "get Bitcoin price"}
</Button>
<div>
<List>
{bitcoinPrice.map((value, i) => (
<div key={i}>
{i === 0 ? "Current price: " : "Previous price: "} {value}
</div>
))}
</List>
</div>
</div>
)
Now I was happy about the styling and I could move forward with optimization of my code
Optimization
During the interview I started thinking whether my code was written with the best practices when it comes to writing functional programming. I did see a flaw in my setAmount function
const setAmount = () => {
const data = await getBitcoin()
if(!data) return
console.log(data)
// {"data":{"base":"BTC","currency":"USD","amount":"19891.09"}}
const {data: { amount }} = data
// New amount is pushed in front of the old amounts that have been deconstructed.
// We then slice the array at 2 so we get rid of any old data we don't want to use anymore
const newPrices = [amount, ...bitcoinPrice].slice(2)
setBitcoinPrice(newPrices)
}
this function is not a pure function... Meaning with the same input we do not always receive the same output. It should also not be modifying any outside variables or have functions as children. I felt like this could be optimized by using composition
Composition or Piping
composing functions means we have a higher order function that executes a multitude of functions on a singular input. Meaning we give parameters to a function that then executes multiple functions in sequence utilizing the parameters, reading from right to left. e.g.
const purchaseItem = compose(
subtractFunds,
emptyBasket,
moveToPurchase,
putInBasket)({name: "Laptop", price: 15})
Piping is the same concept but then the other way around. Instead of right to left we go left to right
const purchaseItem = pipe(
putInBasket,
moveToPurchase,
emptyBasket,
subtractFunds,
)({name: "Laptop", price: 15})
I found a great solution to compose asynchronously. What this means is that I have created a compose function which takes in promises and converts those into actual data
export const compose =
(...functions) =>
(input) =>
functions.reduceRight(
(chain, func) => chain.then(func),
Promise.resolve(input)
);
These powerful few lines create a composition HOF (higher order function) that resolves promises
First function
we give it x amount of functions in the first function of the chain, which we use a spread operator on. so we can have any amount of functions.
Second function
we take input in a second function, so we can curry the input on to the functions as seen in the example above.
Third function
this is what the actual function returns, it is a reducer that takes in chain and function. the chain is used to create a resolve for the functions, and resolves the given input through these chains.
I know right... take a breather after this one.
The Solution
The solution comes down to a couple of core concepts being applied to a core problem, as stated we had the following requirements:
- Create a button that once clicked showcases the current price of Bitcoin
- After the button is clicked the buttons text changes from "get Bitcoin price" to "refresh Bitcoin price"
- After the button is clicked it changes color from blue to purple
- Once the button is clicked a field shows up with "Current price: " after a consecutive click a second field shows up with "Previous price: "
The following paradigms are used to solve the issue:
- modularization
- functional programming
- composing
here is a snapshot of the architecture implemented for this solution
The following files are highlighted:
- App.js - this stores the application
- styles.js - this is holds our
styled-components
- /utils - folder with utility functions, in this case the compose function
- /API - folder with API calls, in this case the bitcoin api
The reason I chose this file structure is that it gives room for growth, utility functions are really used a lot, storing these in an easy to reach folder is beneficial to a team working on an application. Setting up webpack to import from the /src
folder is even better because these folders will be accessible anywhere instead of having the write relative paths.
let's take a close look at App.js
import React, { useState } from "react";
import { getBitcoin } from "./API";
import { compose } from "./utils";
import { Button, List } from "./styles";
import "./App.css";
function App() {
const [hasBeenPressed, setPressed] = useState(false);
const [bitcoinPrice, setBitcoinPrice] = useState([]);
const storeAmount = ({ data }) => {
if (!data) return;
const { amount } = data;
const bitcoinPrices = [amount, ...bitcoinPrice].slice(2);
setBitcoinPrice(bitcoinPrices);
};
const bitcoinCall = compose(storeAmount, getBitcoin);
return (
<div className="App">
<Button
isPressed={hasBeenPressed}
onClick={() => {
if (!hasBeenPressed) setPressed(true);
bitcoinCall();
}}
>
{hasBeenPressed ? "refresh Bitcoin price" : "get Bitcoin price"}
</Button>
<div>
<List>
{bitcoinPrice.map((value, i) => (
<div key={i}>
{i === 0 ? "Current price: " : "Previous price: "} {value}
</div>
))}
</List>
</div>
</div>
);
}
export default App;
as you can see I modularized some of the code, for example we are now importing the compose function as well as the API call for bitcoin, since the bitcoin API call returns a promise we use our special compose function to resolve that promise and pass on the data to the setAmount
function.
const [bitcoinPrice, setBitcoinPrice] = useState([]);
const storeAmount = ({ data }) => {
if (!data) return;
const { amount } = data;
const bitcoinPrices = [amount, ...bitcoinPrice].slice(2);
setBitcoinPrice(bitcoinPrices);
};
const bitcoinCall = compose(storeAmount, getBitcoin);
As you can see I utilized the styled components to create the button and the list components
Button component
<Button
isPressed={hasBeenPressed}
onClick={() => {
if (!hasBeenPressed) setPressed(true);
bitcoinCall();
}}
>
{hasBeenPressed ? "refresh Bitcoin price" : "get Bitcoin price"}
</Button>
as you can see I'm passing on the hasBeenPressed to Button component, which is used in the styled component to change the background.I'm also using it to set the text utilizing a ternary operator.
List component
<List>
{bitcoinPrice.map((value, i) => (
<div key={i}>
{i === 0 ? "Current price: " : "Previous price: "} {value}
</div>
))}
</List>
List is used for styling purposes only, making sure the div's are centered and in a column direction.
Helpers
I always like to create folders for my helper functions so it is structured and easily maintainable. These are the folders I created and I usually use the index.js
format because these are easily accessible.
/utils
export const compose =
(...functions) =>
(input) =>
functions.reduceRight(
(chain, func) => chain.then(func),
Promise.resolve(input)
);
/API
export const getBitcoin = async () => {
try {
const call = await fetch("https://api.coinbase.com/v2/prices/BTC-USD/buy", {
method: "GET",
authorization:
"Bearer abd90df5f27a7b170cd775abf89d632b350b7c1c9d53e08b340cd9832ce52c2c",
});
const response = await call.json();
return await response;
} catch {
return [];
}
};
named exports are easy to import again as well, it also separates the different functions instead of exporting default and having a load of different files.
Thank you
Thank you so much for reading this tutorial, I hope it has given you an insight into how to approach a problem like this.
I know this is an opinionated way of writing code but I believe that if you have a foundation that you are following and a good explanation to why you do the things you do, you are an excellent programmer.
if you want to connect you can do so here:
Posted on July 14, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
January 31, 2022