No Time Dad

A blog about web development written by a busy dad

React Interview Questions - Donation App

I came across this react interview question on one of those “Awesome xyz” repositories on GitHub. I thought it was interesting because it isn’t overly difficult, but it shows that a candidate has experience with React, state management, and HTML, among other things. Of course, I can’t find the repository now but if I do I’ll add a link later.

The problem

Using React, create a donation page app where a user can enter a dollar amount to donate to a given cause. The idea is that it’d be a basic GoFundMe style page.

The app should show the current donations balance, which should update when a user submits a donation. Additionally, the donation page should include a goal amount and how much progress has been made towards that goal. Lastly, the donations page app does not need to have persistent data.

The approach

Re-read the question (a few times)

If I were asked this question in a real interview the first thing I’d recommend doing is slowing down and reading it a couple times. I’m already going to be nervous since it’s an interview and they’re stressful, so it’s likely that my first time reading the question is going to be a skim at best.

The thing is, most places that are interviewing candidates aren’t going to think less of a candidate re-reading the problem a few times. And if they do, I’d consider it a red flag. Plus, spending more time up front to understand the requirements is a better look than completely missing a requirement later on. Which, to be fair, will probably happen anyways.

Create a list of requirements

A lot of times, interview questions are vague. Sometimes this is intentional, and sometimes it’s just a poorly written question. The worst part is that sometimes it’s hard to tell which it is. Sometimes when I’m working on a question like this in an interview I’m adding some requirements that weren’t explicitly defined in the question.

For example, the original question didn’t mention giving the donation page a name or a description. But, this is likely something I’d include in my answer to give the page context. The question also didn’t mention how or if the page should be styled. So, I’d probably apply some css in my final answer but I wouldn’t go overboard since the interviewer didn’t ask. My first pass at creating the app will be completely unstyled.

With that being said, it’s super important that I meet all of the requirements of the question as written. I can’t emphasize this enough. It’s easy to get distracted by what I think the unstated requirements are, and miss the actual requirements altogether.

At this point I’d start turning the original interview question into a list of requirements. Sometimes the interviewer will do this for you, but I’ve found that’s not always the case. I like to keep some paper and pencil next to me so I can write down the requirements. Typing them out if I’m in a live coding session is good too. Trying to store them in my head is not good. Always leads to missing requirements.

As far as the requirements that weren’t mentioned goes, I typically don’t write those down, at least not in view of the interviewer anyways. I’ll try to keep a mental note of what I think they are so I can add them later.

The nice thing is that if I forget about these unstated requirements it’s usually not the end of the world since they weren’t explicitly stated. Emphasis on usually the case here. Sometimes interviewers are looking for requirements that weren’t explicitly stated. Navigating that problem comes with interview experience.

With all that said, here is the list of requirements I’d write or type out for this question during an interview based on the original question.

  • Show current donations balance on page load
  • Update current balance on submit
  • Display a donations goal for the page
  • Show current progress towards that goal

Below is the list of requirements that I think are needed but not explicitly asked for. Again, I would not write these down or type them out. If time allows during the interview, I’d implement them. Otherwise they’re thrown out to focus on the stated requirements.

  • Include title for the cause being donated to
  • Include a description
  • Add some basic styles to the page so it’s not just default elements

Start building

Once I have the requirements it’s time to get to work building the app. The dev environment for each interview will probably be different, so for this example I’m going to keep everything in a single file and assume it’s part of a larger React application that’ll render the component somewhere on a page. The first thing I’d do is create a new .jsx file, add imports, and add a placeholder component.

The question description mentioned that persistent state isn’t required, so I can safely assume React’s state will be enough for this app. Which means I’ll need to import the react useState hook to manage the state.

As far as the react component itself goes, I’ll be using a functional component instead of a class based component. This is sort of personal preference, but React is definitely shifting towards functional components and the choice between the two might be something the interviewer is looking out for.

Since I’ll be using the useState hook, I know that my component needs to be defined with curly braces and a return statement. Which is an alternative to just returning jsx. Using the explicit return statement allows me to add declarations in the component, where I wouldn’t be able to do that if the component just returned jsx.

export const Donations = () => {
  ...
  return ( ... )
};

Compared to:

export const Donations = () => ( ... )

At this point I have a file called donations.jsx that looks like the below. In the interview I’m likely going to keep the app to a single file and single component even though in a real-life app it might be made up of many files and components. If time permitted at the end of the interview or the interviewer asked, I could work on breaking the component up if needed.

// donations.jsx
import React, { useState } from "react"

export const Donations = () => {
  return <h1>Donations</h1>
}

Donations

The state object

There are potentially three different state values to be tracked for this donation app. The donation goal, the current donation balance, and the percentage of the goal reached. However, I don’t think it’s necessary to track all three. In fact, I think only one of them needs to be tracked in the stage object, and that’s the current balance.

The donation goal is a value that won’t be changed by a user visiting the page to donate. It’s controlled by the owner of the page. It’s essentially hard-coded unless the owner of the page updates it. So that’s what I’ll do here too.

The percentage of the donation goal completed is a value derived from the donations goal value and the current donations value. Meaning, it’s a calculation. And one that I’ll let React handle for me each time the value of the current balance is updated.

All of this is to say that my state object will be very simple. A single value, in fact. React’s useState makes this easy. I’ll also add a static value outside of the state for required donations. In a real application this value would come from a backend somewhere. But in an interview question like this it’ll just be hard-coded.

The updated code will look as follows:

// donations.jsx
import React, { useState } from "react"

export const Donations = () => {
  const requiredDonations = 50000
  const [currentDonations, setCurrentDonations] = useState(25000)
  return <h1>Donations</h1>
}

Displaying the values

The next thing to do is display the values I just created. Which is easy enough, but there is a small problem worth mentioning. The goal and current donations are a raw number and not in the correct format a user would expect. For example $50000 vs $50,000.

The easy way to convert a number to currency with JavaScript is to use Number(n).toLocaleString() with a $ in front:

const currency = `$${Number(requiredDonations).toLocaleString()}`

Or in React:

<div>Goal: ${Number(requiredDonations).toLocaleString()}</div>

But, I only knew this because I googled it from the comfort of my home. I might not have that luxury in an interview. In that case, I think it’s okay to say “I’m not sure how to do this off of the top my head” during the interview and leave them as numbers. I personally don’t think it’s reasonable to expect candidates to have the entire JavaScript API memorized. And it isn’t a true reflection of real world work.

Below is how my Donations component in donations.jsx looks now after adding the code to display the values:

// donations.jsx
import React, { useState } from "react"

export const Donations = () => {
  const requiredDonations = 50000
  const [currentDonations, setCurrentDonations] = useState(25000)
  return (
    <h1>Donations</h1>
    <div>Goal: ${Number(requiredDonations).toLocaleString()}</div>
    <div>Donations: ${Number(currentDonations).toLocaleString()}</div>
    <div>
      Progress: {(currentDonations / requiredDonations) * 100}% reached
    </div>
  )
}

Donations

Goal: $50,000
Donations: $25,000
Progress: 50% reached

User interaction

At this point the Donation component only displays values. There isn’t any user interaction. I need the component to be able to take user input in the form of a dollar amount to be donated. The input should update the other values displayed to the user.

So, I need an input element. I also need a way to submit the input, so I need a button too. Both of which should be part of a form element, which is best practice when dealing with inputs. The interviewer will likely be looking for this type of detail.

I’ll add the form just as vanilla HTML, without any interaction with the state values. It’ll look something like the below. It has a form element wrapper, a label, an input, and a submit button. Note that I added a min attribute to the input element, which prevents the user from submitting negative numbers or zero. It’s a good idea to talk through details like this during the interview.

<form>
  <label htmlFor="donation-amount">Amount</label>
  <input type="number" min="{0}" />
  <button type="button">Submit</button>
</form>

The full component code looks as follows:

// donations.jsx
import React, { useState } from "react"

export const Donations = () => {
  const requiredDonations = 50000
  const [currentDonations, setCurrentDonations] = useState(25000)
  return (
    <h1>Donations</h1>
    <div>Goal: ${Number(requiredDonations).toLocaleString()}</div>
    <div>Donations: ${Number(currentDonations).toLocaleString()}</div>
    <div>
      Progress: {(currentDonations / requiredDonations) * 100}% reached
    </div>
    <form>
      <label htmlFor="donation-amount">Amount</label>
      <input type="number" min={0}/>
      <button type="button">Submit</button>
    </form>
  )
}

At this point the interviewer might ask why the button is of type “button” instead of “submit”. Which is a valid question, and one I’d ask too. I try to be mentally ready for questions like this and try not to let it throw me off. My answer to this question would be that a type="submit" button would refresh the page and that is not something I want to happen because this simple version of the donation page will revert to the default values on page refresh since there isn’t a backend to save the data. The button should only update the state and do nothing else.

Now it’s time to convert the form to be a React controlled component, thus adding interactivity. I’ll do this by adding a new state value to track the input value, and an onChange handler to the input to update that state value. I’ll also add an onClick handler to the button which will update the currentDonations state value based on the value of the user entered input amount.

And of course, I’d try to talk through each of these changes to the form as I’m adding them. Explaining why I wrote the click handler and on change handler the way I did, mentioning binding and event objects.

// donations.jsx
import React, { useState } from "react"

export const Donations = () => {
  const requiredDonations = 50000
  const [currentDonations, setCurrentDonations] = useState(25000)
  const [donationAmount, setDonationAmount] = useState(50)
  return (
    <h1>Donations</h1>
    <div>Goal: ${Number(requiredDonations).toLocaleString()}</div>
    <div>Donations: ${Number(currentDonations).toLocaleString()}</div>
    <div>
      Progress: {(currentDonations / requiredDonations) * 100}% reached
    </div>
    <form>
      <label htmlFor="donation-amount">Amount</label>
      <input
        type="number"
        min={1}
        value={donationAmount}
        onChange={event => setDonationAmount(event.target.value)}
      />
      <button
        type="button"
        onClick={() => setCurrentDonations(currentDonations + donationAmount)}
      >
        Submit
      </button>
    </form>
  )
}

Donations

Goal: $50,000
Donations: $25,000
Progress: 50% reached

Now I have an interactive form that takes a user input amount and updates the donations total and progress percentage when the submit button is clicked. However, there’s a problem. If I enter a value of 53, for example, and click submit I see the below displayed. Which is clearly wrong.

donations_bad

The first feeling that hits my brain at this point during an interview is panic. I feel pressure now because I’m trying to show the interviewer how good of an engineer I am, and I’ve gone and made a mistake. But the important thing during an interview when this happens is how I recover from it. In fact, the interviewer might be expecting mistakes to see how I handle it and debug the issue.

It’s a really good idea at this point to take some deep breaths. Then I can calmly try to start looking into what the issue is. It’s also important for me to talk through my debugging process. As I said before, the interviewer might be looking for me to make a mistake so they can see my debugging process.

So, I’ll start thinking about what is causing my displayed values to be wrong. I feel confident that my calculations are correct, so it must be an issue with the input element value. Which immediately draws my attention to the event object, specifically the event.target.value value.

Off the top of my head, I wonder if the value of event.target.value is actually a string instead of a number. That would definitely cause problems for the calculations and explain the strange numbers being displayed.

There are a couple different ways I could debug this. A breakpoint, a console.log(event.target.value), or maybe there is some JavaScript function that can help me. Which there is. And I happen to know what it is. But if I didn’t, I’d go down the road of setting a breakpoint. Honestly, I might do that anyways to show the interviewer I know how to debug without using console.log statements.

The value of event.target.value is indeed a string. And I need to add a parseFloat function call to convert it to a number before setting the value in the state.

<input
  type="number"
  min={1}
  value={donationAmount}
  onChange={event => setDonationAmount(parseFloat(event.target.value))}
/>

A good interviewer might ask why I used parseFloat instead of parseInt? And the answer should be that I’m allowing decimal values in the input and parseInt will cut off the trailing decimal, which I don’t want to happen.

A good interviewer might also ask if there is anything I need to be careful about when using parseFloat. To that I’d say that I do have to ensure the value being passed to parseFloat is a number. Which the input element takes care of for me because it has the attribute type="number".

Below is the working code and component that meets the requirements from the original question.

Donations

Goal: $50,000
Donations: $25,000
Progress: 50% reached
// donations.jsx
import React, { useState } from "react"

export const Donations = () => {
  const requiredDonations = 50000
  const [currentDonations, setCurrentDonations] = useState(25000)
  const [donationAmount, setDonationAmount] = useState(50)
  return (
    <h1>Donations</h1>
    <div>Goal: ${Number(requiredDonations).toLocaleString()}</div>
    <div>Donations: ${Number(currentDonations).toLocaleString()}</div>
    <div>
      Progress: {(currentDonations / requiredDonations) * 100}% reached
    </div>
    <form>
      <label htmlFor="donation-amount">Amount</label>
      <input
        type="number"
        min={1}
        value={donationAmount}
        onChange={event => setDonationAmount(parseFloat(event.target.value))}
      />
      <button
        type="button"
        onClick={() => setCurrentDonations(currentDonations + donationAmount)}
      >
        Submit
      </button>
    </form>
  )
}

Wrapping up

The donations component is far from perfect. If I had time I’d add the items on my secondary requirements list. I’d also ensure the numbers were always formatted correctly. The important thing is that I produced something, and it works.

Overall, the best strategy during an interview is to try to stay calm and give yourself time to think through a problem. Interviewers might ask intermediate questions as you work through a problem, but they might not. Expect that you will make mistakes and be prepared to work through them. Lastly, talk through everything. The interviewer wants to know how you work through problems and they can’t read your mind.