Calculator Buttons

Write a component that runs a user-selected operation on a number.


Environment

Use the environment you are most comfortable with. I recommend using create-react-app to create a local version of the project so that you can inspect the DOM easily from your browser when debugging. Alternatively, you can work from a blank React template in CodeSandbox. Starter code is provided after the problem specification.

Specification

Write a functional component that renders five buttons, a list displaying the user’s program, a form to accept a starting value, and the result of running the program on said value. The first four buttons are numeric operations that the user may add to their program sequence (half, double, increment, decrement), and the corresponding functions are provided in the starter code. When a button is pressed, its operation should be added to the end of the user’s program. The component in its starting state (with an empty program sequence) is shown below:

Component starting state
Component starting state

The following screenshot was taken after the following sequence of user interactions: click the double, increment, and half buttons in that order, enter the value 199.5 in the input field to the left of the submit button, and click Submit.

Component after run
Component after run

As you may have been able to infer from the image, the component should leave the user’s function as is but clear the starting value after the user selects submit and their program runs.

Starter Code

Here is some code to get you started. The functions whose operations are defined by the button names are included as global variables.

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

function App() {
  return <CustomProgram />;
}

const half = (number) => number / 2;
const double = (number) => number * 2;
const increment = (number) => number + 1;
const decrement = (number) => number - 1;

const CustomProgram = () => {
  // YOUR CODE HERE
};

export default App;

Solution

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

function App() {
  return <CustomProgram />;
}

const half = (number) => number / 2;
const double = (number) => number * 2;
const increment = (number) => number + 1;
const decrement = (number) => number - 1;

const CustomProgram = () => {
  const [startVal, setstartVal] = useState();
  const [funcList, setfuncList] = useState([]);
  const [prevExecution, setprevExecution] = useState("? -> My Function -> ?");

  const onSubmit = (e) => {
    e.preventDefault();
    const res = funcList.reduce(
      (prevVal, func) => eval(func)(prevVal),
      startVal
    );
    setprevExecution(startVal + " -> My Function -> " + res);
    setstartVal("");
  };

  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        padding: 60,
      }}
    >
      <div
        style={{
          display: "flex",
          flexDirection: "column",
          textAlign: "center",
        }}
      >
        <FuncButtons funcList={funcList} setfuncList={setfuncList} />
        <h2>My Function</h2>
        {funcList.map((funcName) => (
          <p1>{funcName}</p1>
        ))}

        {/* Start value input form: */}
        <div
          style={{
            display: "flex",
            flexDirection: "row",
            justifyContent: "center",
            margin: 60,
          }}
        >
          <form onSubmit={onSubmit}>
            <input
              type={"number"}
              required
              value={startVal}
              min={-500}
              max={500}
              step={0.001}
              onChange={(e) => setstartVal(e.target.value)}
              style={{ width: 92 }}
            />
            <input type={"submit"} style={{ width: 100 }} />
          </form>
        </div>
        <div style={{ margin: 20 }}>Last execution:</div>
        <div>{prevExecution}</div>
      </div>
    </div>
  );
};

const FuncButtons = ({ funcList, setfuncList }) => {
  return (
    <div
      style={{
        display: "flex",
        flexDirection: "row",
        justifyContent: "center",
      }}
    >
      <button onClick={() => setfuncList([...funcList, "half"])}>half</button>
      <button onClick={() => setfuncList([...funcList, "double"])}>
        double
      </button>
      <button onClick={() => setfuncList([...funcList, "increment"])}>
        increment
      </button>
      <button onClick={() => setfuncList([...funcList, "decrement"])}>
        decrement
      </button>
      <button style={{ marginLeft: 10 }} onClick={() => setfuncList([])}>
        Clear
      </button>
    </div>
  );
};

export default App;

Because I have explained the architecture of a form as part of previous coding interview challenges, I will focus this discussion of my solution on the other elements of the page. For more information on using the native HTML form and input elements, refer to those articles.

Now, the solution. I first instantiate state variables for the input field value, the user’s current program (an array of operation names), and a string displaying the previous execution of the program. When the user submits, I make use of Javascript’s Array.prototype.reduce() function to run the user’s program on their starting value. In short, reduce() provides programmers with a way to convert an array into a single output using a custom function provided by the programmer. The function accepts two arguments, a function and a starting value. The function passed to reduce may accept up to four arguments: previousValue, currentValue, currentIndex, array, but the function I pass to reduce only makes use of the first two arguments, previousValue and currentValue, which correspond to the cumulative result of the reduction so far and the current element in the iteration. previousValue is initialized to the starting value when one is provided, and it is updated to the result of the callback function (given the previous and current values) until the end of the array is reached. I use Javascript’s eval() function to retrieve the functions corresponding to the strings in the funcList array. Finally, I separated the four operation buttons and the clear program button into a separate FuncButtons component and set their onClick attributes to update the user’s program accordingly.


useState
useEffect