Code a Harry Potter Sorting Hat with React.js

Introduction to React Hooks

Code a Harry Potter Sorting Hat with React.js

Intro:

I decided to make a Harry Potter sorting hat because someone asked for it on "What to code". A lot of the sorting hats online are Buzzfeed style quizzes which are sort of bland and all look alike. One thing was good enough to give me an idea on how to start is that the sorting process is more or less like a personality test where you get to answer a bunch of questions and get to know which personality you are at the end. P.S. I made a video about this whole thing if you prefer video format, so check it out:

I also provided the final code for download on Github: Sorting Hat Minimal

The code:

I'm assuming you're familiar with React at least a bit, so there aren't much details covering the basics (Nevertheless, you may read ahead for some tips). Since this is a small project, we'll use create-react-app (Not recommended for big scale/complex projects since it's hard to configure later + the more you add to it, it gets bloated fast)

Once it finishes setting things we should have something like this: image.png

Most of the code is going to be in App.js, so once we get rid of the default code, we end up with this:

import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
    </div>
  );
}

export default App;

To understand the code, you have to understand that the app will consist of 3 parts: The starting screen --> The Quiz itself --> The result. And the way we can handle that is by using a "Ternary Operation" which is the same thing as a "if" statement, and it's written as such:

{ boolVariable ? (
        "Do this if boolVariable is true"
      ) : (
        "Do this if boolVariable is false"
      )}

We can use it in our code like this: We set a showHouse variable that has a value of false at first, meaning the quiz isn't over, then inside it we put a showQuiz variable that also has a value of false, until we click a 'Start' button. Here's the code for a better explanation:

{ showHouse ? (
        // The code to show which house you belong to
      ) : (
        { showQuiz ? (
          //The code for the quiz goes here
          ) : (
          //The starting screen + 'Start button' goes here
        )}
      )}

Most of the code is pretty simple if you know the basics, so I'll skip explaining the <div> elements and whatnot, but in case I'm missing something, please let me know. You can find the entire code on GitHub: Sorting Hat Minimal

React Hooks:

1. useState:

useState is a react feature used to handle the state of a variable/component regardless of how complex the logic in your code is. If you're familiar with JavaScript classes, you can think of it as a replacement to those. Otherwise, if you're a beginner, you can look at the following example for a simpler explanation of the most common use of useState.

import  {useState} from 'react';

export default function App() {
  const [counter, setCounter] = useState(0);

  return (
    <div style={{textAlign: 'center'}}>
      <h1>Counter App</h1>
      <h3>Click the button to increment:</h3>
      <h4>{counter}</h4>
      <button onClick = {() => setCounter(counter + 1)}>Click Me!</button>
    </div>
  );
}

useState is made of three parts: a variable counter, a setter setCounter, which is the part that we can use to change the variable, and the initial state of the variable '0'. The initial state depends on the type of variable, so if the variable is a boolean, the initial state can either be true or false, and so on. And the way we call useState is everytime I click the button onClick, I tell setCounter to increment the counter by 1 counter + 1.

In the sorting hat, we use useState 9 times:
For showing the quiz after the Start button:

const [showQuiz, setShowQuiz] = useState(false);

For incrementing the question counter after each answer:

const [currentQuestion, setCurrentQuestion] = useState(0);

For updating the total points per house:

    const [totalGryffindor, setGryffindor] = useState(0);
    const [totalSlytherin, setSlytherin] = useState(0);
    const [totalRavenclaw, setRavenclaw] = useState(0);
    const [totalHufflepuff, setHufflepuff] = useState(0);

For updating which house the player belongs to after each question:

const [house, setHouse] = useState("Muggle");

For updating the house banner that shows with result:

const [banner, setBanner] = useState('')

For showing the result after the quiz is over:

const [showHouse, setShowHouse] = useState(false);

(This part can be updated with more details if needed, so let me know)

2. useEffect:

A lot of times, the events that happen within an app/website come from the interaction of the user with it. However, many other events often happen in the background without the direct interference of the user, such as data fetching, network requests and so on, so you can’t implement a simple straightforward logic to handle the effects that come with those updates. These events are "side-effects" to other actions, hence the naming of the hook useEffect which allows us to perform such actions through code.

The simplest example to allow to you understand what it does is this:

import  {useState, useEffect} from 'react';

export default function App() {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${counter} times`;
  });

  return (
    <div style={{textAlign: 'center'}}>
      <h1>Counter App</h1>
      <h3>Click the button to increment:</h3>
      <h4>{counter}</h4>
      <button onClick = {() => setCounter(counter + 1)}>Click Me!</button>
    </div>
  );
}

You can see that it's the same previous example where we have a counter. This time we added useEffect to update the page title that shows on the tab. We can do so by accessing the browser API like this:

document.title = `You clicked ${counter} times`;

By default, useEffect runs with each render that happens in the page, but if you want to customize it to run under specific conditions, you can see the official React documentation for more details: React Hooks

In our example, changing the banner is something that happens in the background AFTER the house is updated. There could be other ways to implement a logic to handle the change, but I chose to use useEffect to show how it works. For that I wrote a function called changeBackground that looks like this:

const changeBackground = () => {
        if (house === 'Gryffindor' && showHouse===true) {
            setBanner(require("./assets/banners/Gryffindor.jpg"));}
        if (house === 'Slytherin' && showHouse===true) {
            setBanner(require("./assets/banners/Slytherin.jpg"));}
        if (house === 'Ravenclaw' && showHouse===true) {
            setBanner(require("./assets/banners/Ravenclaw.jpg"));}
        if (house === 'Hufflepuff' && showHouse===true) {
            setBanner(require("./assets/banners/Hufflepuff.jpg"));}
    };

What it does is, each time there's a change, it checks both the house variable according to the overall score, and whether the showHouse is true AKA whether the quiz is over or not, so by the end, when two conditions are met, let's say your house is Slytherin, and the quiz is over so showHouse=true, it sets the banner to the Slytherin banner:

setBanner(require("./assets/banners/Gryffindor.jpg"));}

and the way we make sure changeBackground is constantly running with each render is to put it inside a useEffect function:

useEffect(() => {
        changeBackground()
    });

Result and conclusion:

Finally, after adding our CSS to the code the result is this: image.png

This is my final version of the app (the mobile view is a bit wonky, but that's because I should've started with mobile styling first, oops :p): Sorting Hat I uploaded a code with minimal styling so that you can add your own: Sorting Hat Minimal

Check it out and try it out. Make sure you have node.js installed first. Share your thing if you happen to use the code, and if you have any questions or things you think should be added/removed here, let me know (there's always room for improvement).

~ Qarnax

 
Share this