Virtual Keypad
Hide a page behind a virtual keypad lock.
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 accepts two props: a 4-digit combination and a React component. Your component should display a keypad that allows the user to attempt to enter the 4-digit combination and shows the combination as they enter it above the keypad. If the combination is entered correctly, the site should display the component that is passed to it as a prop (and remove the keypad). The component in its initial state is pictured below:
After the users clicks four numbers, if the combination entered does not match the combination passed to the component in the first prop, issue an alert as pictured below and reset the keypad.
Starter Code
Here is some code to get you started:
import React, { useState } from "react";
function App() {
const unlockedScreen = () => (
<div style={{ textAlign: "center" }}>You are logged in</div>
);
return (
<CombinationLock combination={[1, 2, 3, 4]} NextScreen={unlockedScreen} />
);
}
const CombinationLock = ({ combination, NextScreen }) => {
// YOUR CODE HERE
};
export default App;
Initial values for the two props are included as well. Feel free to divide the challenge into multiple subcomponents.
Solution
import React, { useState } from "react";
function App() {
const unlockedScreen = () => (
<div style={{ textAlign: "center" }}>You are logged in</div>
);
return (
<CombinationLock combination={[1, 2, 3, 4]} NextScreen={unlockedScreen} />
);
}
const CombinationLock = ({ combination, NextScreen }) => {
const [entered, setEntered] = useState([]);
const [loggedIn, setLoggedIn] = useState(false);
const combinationsMatch = (combo) => {
console.assert(combo.length == 4);
for (let i = 0; i < combination.length; i++) {
if (combination[i] != combo[i]) return false;
}
return true;
};
const numberPress = (number) => {
const newCombo = [...entered, number];
setEntered(newCombo);
if (newCombo.length == 4) {
if (combinationsMatch(newCombo)) setLoggedIn(true);
else {
setEntered([]);
alert("Incorrect combination");
}
}
};
const DigitRow = ({ startVal }) => {
return (
<div style={{ display: "flex" }}>
{[startVal, startVal + 1, startVal + 2].map((num) => (
<div
style={{ height: 150, width: 150, borderStyle: "solid" }}
onClick={() => numberPress(num)}
>
{num}
</div>
))}
</div>
);
};
return loggedIn ? (
<NextScreen />
) : (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
textAlign: "center",
marginTop: 100,
fontSize: 100,
}}
>
<div style={{ height: 150, width: 450, borderStyle: "solid" }}>
{entered.join("")}
</div>
<DigitRow startVal={1} />
<DigitRow startVal={4} />
<DigitRow startVal={7} />
<div
style={{
textAlign: "center",
height: 150,
width: 150,
borderStyle: "solid",
}}
onClick={() => numberPress(0)}
>
0
</div>
</div>
);
};
export default App;
We first establish two state variables: an array to store the user’s combination as they enter it and a boolean that is set to true after the user enters the correct combination. The component conditionally renders the keypad or the component passed via props depending on the boolean's value. We define two helper functions in the component. combinationsMatch(combo)
determines if the argument passed matches the correct combination, and numberPress(number)
enables us to create onClick
functions for each of the keypad’s digit with repeating code. It accepts a digit, appends that digit to the currently entered combination, and handles submission behavior if the digit entered is the fourth in the combination. I also separate the HTML for a row of three digits into a separate functional component called DigitRow
defined within the CombinationLock
component for better readability. The formatting of the digits (as a column containing five rows: a row for the current combination, three rows with three digits each, and a row just containing 0) is done with flexbox.