CAPTCHA

Write a CAPTCHA component.


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 to prompt a user with a CAPTCHA test, or a “Completely Automated Public Turing Test To Tell Computers and Humans Apart”. This component will be a bare-bones version of the tests you have likely encountered online that ask you to check a box, select all images of crosswalks or busses, etc. In the starter code, I have provided URLs for six images (pictures of digits 1–6). Render a button that, when pressed, displays six images and prompts the user to select one of them; the number that the user is asked to select should be generated randomly when the user clicks the button. If the user clicks the correct image, the component should return to its initial state (only a button, closed modal). If they click the wrong image, issue an alert in the browser, but leave the modal open for the user to try again.

GIF of final product
GIF of final product

Starter Code

Here is some code to get you started:

import React, { useState } from "react";

const ONE =
  "https://images.pexels.com/photos/2249528/pexels-photo-2249528.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2";
const TWO =
  "https://images.pexels.com/photos/1061141/pexels-photo-1061141.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2";
const THREE =
  "https://images.pexels.com/photos/2249530/pexels-photo-2249530.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2";
const FOUR =
  "https://images.pexels.com/photos/1061139/pexels-photo-1061139.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2";
const FIVE =
  "https://images.pexels.com/photos/1010973/pexels-photo-1010973.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2";
const SIX =
  "https://images.pexels.com/photos/4772874/pexels-photo-4772874.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2";

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

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

export default App;

For readability, you may choose to split your code into more than one component.

Solution

import React, { useState } from "react";

// CONSTs in Starter Code

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

const Captcha = () => {
  const [open, setOpen] = useState(false);
  const [answer, setAnswer] = useState(1);

  return (
    <div style={{ display: "flex", justifyContent: "center" }}>
      <button
        style={{ zIndex: 1, marginTop: 200 }}
        onClick={() => {
          setOpen(true);
          setAnswer(Math.floor(Math.random() * 5 + 1));
        }}
      >
        I'm not a robot
      </button>
      {open && (
        <div
          style={{
            display: "flex",
            justifyContent: "center",
            flexDirection: "column",
            marginTop: 100,
            position: "fixed",
            zIndex: 2,
          }}
        >
          <Images answer={answer} setOpen={setOpen} />
        </div>
      )}
    </div>
  );
};

const Images = ({ answer, setOpen }) => {
  const isAnswer = (id) => {
    id == answer ? setOpen(false) : alert("Intruder!");
  };

  return (
    <>
      <div style={{ alignSelf: "center", fontSize: 20 }}>
        {"Select " + answer}
      </div>
      <div style={{ display: "flex", justifyContent: "center", flex: "row" }}>
        <img
          src={ONE}
          style={{ width: 200, height: 200 }}
          onClick={() => isAnswer(1)}
        />
        <img
          src={TWO}
          style={{ width: 200, height: 200 }}
          onClick={() => isAnswer(2)}
        />
        <img
          src={THREE}
          style={{ width: 200, height: 200 }}
          onClick={() => isAnswer(3)}
        />
      </div>
      <div style={{ display: "flex", justifyContent: "center", flex: "row" }}>
        <img
          src={FOUR}
          style={{ width: 200, height: 200 }}
          onClick={() => isAnswer(4)}
        />
        <img
          src={FIVE}
          style={{ width: 200, height: 200 }}
          onClick={() => isAnswer(5)}
        />
        <img
          src={SIX}
          style={{ width: 200, height: 200 }}
          onClick={() => isAnswer(6)}
        />
      </div>
    </>
  );
};

export default App;

The reference solution breaks up the problem into two components. The Images component accepts two props, a number corresponding to the correct image and a function to open/close the six-image modal. It renders two rows of three images each. To succinctly write the onClick functions for each image, I reuse a helper function titled isAnswer that closes the component or issues an alert given the image clicked and the correct answer. The enclosing component, Captcha, establishes the state for whether the modal is open and the current answer to the modal. It renders a button and conditionally renders the modal on top of it based on the value of the open state variable. When the user clicks the button, the answer is set to a random integer between 1 and 6, and the modal is opened. I make use of the CSS z-index property to layer the images on top of the button. If this challenge was too simple, attempt to order the images randomly in the modal each time it is opened as opposed to the same, fixed order each time.


useState