<!-- DO NOT REMOVE or change the ID of following div. It is used as placeholder for the app -->
<div id="app"></div>

html,
body {
  padding: 0;
  margin: 0px;
  display: flex;
  justify-content: center;
  font-family: Roboto, sans-serif;
  flex-direction: column;
}
@media (max-width: 320px) {
  h1 {
    font-size: 14px;
  }
}
#root {
  max-width: 400px;
}
h1 {
  border-style: solid;
  border-width: 0 2px 2px 2px;
  margin-top: 0;
  box-shadow: 0px 3px 4px gray;
  padding: 0 10px;
}
button {
  margin: 5px;
  padding: 5px 10px;
  font-size: 15px;
  border-radius: 5px;
  border: none;
  border-top: 2px solid green;
  background-color: lightgreen;
  height: 40px;
  min-width: 100px;
}

button.bad {
  border-top: 2px solid #880617;
  background: #f88493;
  color: #880617;
}

button.dangerous {
  border-top: 2px solid darkred;
  background-color: red;
  color: white;
}
.robotContainer {
  display: flex;
  justify-content: center;
  align-items: center;
}

.robotContainer img {
  border: 2px solid;
  border-radius: 5px;
}

.controls {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
}

.robotSelectorContainer {
  text-align: center;
  margin-bottom: 10px;
}

select {
  font: inherit;
  padding: 0.5rem 1rem;
  border-radius: 5px;
  height: 40px;
}

select.good {
  border: 2px solid green;
  background: lightgreen;
  color: green;
}

select.bad {
  border: 2px solid #880617;
  background: #f88493;
  color: #880617;
}

select:focus {
  outline: none;
}

/************************************/
//            Robot COMPONENT       
/************************************/
const Robot = (props) => {
  const [loadedRobot, setLoadedRobot] = React.useState({});
  const [isLoading, setIsLoading] = React.useState(false);

  const fetchData = () => {
    setIsLoading(true);
    fetch(`https://robohash.org/${props.selectedRobotId}`, {
      "Content-type": "application/octet-stream"
    })
      .then(response => {
        if (response.status !== 200) {
          throw new Error("Could not fetch the Robot");
        }
        return response.blob();
      })
      .then(robot => {
        setIsLoading(false);
        setLoadedRobot(window.URL.createObjectURL(robot));
      })
      .catch(err => console.error(err));
  };

  React.useEffect(() => {
    console.log('Robot: componentWillUpdate');
    fetchData();
  }, [props.selectedRobotId]);

  React.useEffect(() => {
    console.log('Robot: componentDidMount');
    return () => {
      console.log('Robot: componentWillUnmount');
    }
  }, []);
  let content = <p>Loading robot...</p>;
    if (!isLoading && loadedRobot) {
      content = (
        <img
          width="150"
          src={loadedRobot}
          alt=""
          onLoad={function(event) {
            window.URL.revokeObjectURL(event.target.src);
          }}
        />
      );
    } else if (!isLoading && !loadedRobot) {
      content = <p>Failed to load robot!!!</p>;
    }

    return <div className="robotContainer">{content}</div>;
}
/************************************/
//     RobotSelector COMPONENT     
/************************************/
const RobotSelector = ({onRobotChange, selectedRobotsId, robotType}) => {
  const [robots, setRobots] = React.useState([]);
  const [isLoading, setIsLoading] = React.useState(false);
  React.useEffect(() => {
    setIsLoading(true);
    fetch("https://swapi.co/api/species")
      .then(response => {
        if (!response.ok) {
          throw new Error("Failed to fetch.");
        }
        return response.json();
      })
      .then(robotData => {
        const selectedRobots = robotData.results.slice(0, 5).map((robot, index) => ({
            name: robot.name,
            id: index + 1
          }));
          setRobots(selectedRobots);
          setIsLoading(false);
        })
      .catch(err => {
        console.log(err);
      });
  }, []);

  let content = <p>Loading Robots...</p>;
  if (
    !isLoading && robots && robots.length > 0
  ) {
    content = (
      <select
        onChange={onRobotChange}
        value={selectedRobotsId}
        className={robotType}
        >
        {robots.map(robot => (
          <option key={robot.id} value={robot.id}>
            {robot.name}
          </option>
        ))}
      </select>
    );
  } else if (
    !isLoading && (!robots || robots.length === 0)
  ) {
    content = <p>Could not fetch any data.</p>;
  }

  return <div className="robotSelectorContainer">{content}</div>;
}
/************************************/
//            App COMPONENT        
/************************************/
const App = () => {
  const [selectedRobotId, setSelectedRobotId] = React.useState(1);
  const [robotType, setRobotType] = React.useState("good");
  const [destroyed, setDestroyed] = React.useState(false);
  
 const onRobotChange = event => {
    const robotId = event.target.value;
    setSelectedRobotId(robotId);
  };
  const onRobotTypeChange = robotType => {
    setRobotType(robotType);
  };
  const onDestruction = () => {
    setDestroyed(true);
  };

  if (destroyed) {
    return <h1>This bad robot destroyed everything!</h1>;
  }
  return (
    <React.Fragment>
      <h1>React Lifecycle with hooks</h1>
      <RobotSelector
        robotType={robotType}
        selectedRobotId={selectedRobotId}
        onRobotChange={onRobotChange}
        />
      <Robot selectedRobotId={selectedRobotId} />
      <div className="controls">
        <button onClick={onRobotTypeChange.bind(this, "good")}>
          Good Robot
        </button>
        <button
          className="bad"
          onClick={onRobotTypeChange.bind(this, "bad")}
          >
          Bad Robot
        </button>
        {robotType === "bad" && (
          <button className="dangerous" onClick={onDestruction}>
            Destroy
          </button>
        )}
      </div>
    </React.Fragment>
  );
}

ReactDOM.render(<App />, document.getElementById("app"));

View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js