<!-- 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
This Pen doesn't use any external CSS resources.