Reactémon: Mutation Primer

Reactémon: Mutation Primer

Welcome to Reactémon, a world inhabited by JSX. For some people React is for pet projects, Others use them to fight... Wait, hang on this is just the intro to Pokémon. Well, since we're here, we may as well roll with it. This series of posts aims to explain some core concepts of React using the nostalgia and charm of everyone's favourite monster fighting game.

Before we dive head first into specific React concepts we first need to cover some basics, so this is a primer on evolution mutation!

I choose you, Pikachu!

This is my Pikachu, there are many others like it, but this one is mine!

Each Pokémon in our party is important and wonderfully unique. Maybe we hunted for the perfect EV's, maybe it clinched a win when the battle looked dire, maybe we just like the way it looks. When it wins an important battle, learns a new move, or you give it a goofy nickname it doesn't change the fact that it's still the same Pikachu.

In Javascript we have a concept for this. They're called reference types. In JavaScript, objects and arrays are considered reference types Just like Pokémon, when we change an attribute of it we don't create a whole new version of it, we just modify the original which we call mutation. This means that no matter where we use it, it's still the same instance. In order to do this we store a reference to the original when we assign it to a variable or pass it into a function.

const myFirstPokemon = {
    name: "Pikachu",
    level: 99,
}
const myStrongestPokemon = myFirstPokemon;
myStrongestPokemon.nickName = "Sparky";
console.log(myFirstPokemon.nickName); // Sparky

We can see this in action in the code above. When we set myStrongestPokemon to myFirstPokemon we are not creating a new Pikachu, we are setting the value of the variable to a reference of where the original Pikachu is stored. This means when we then add a nickname to myStrongestPokemon it is also added to myFirstPokemon because they are both pointing to the same Pikachu.

const myFirstPokemon = {
    name: "Pikachu",
    level: 1,
}

const levelUpPokemon = (pokemon) => {
    pokemon.level += 1;
}

levelUpPokemon(myFirstPokemon);
console.log(myFirstPokemon.level); // 2

We can see this holds true when we pass a reference variable as a parameter. We are not creating a new Pikachu, we are just passing information on where the original Pikachu is stored.

Types

The type system has been a staple of the Pokémon series right from the start and is a core part of every game. It's basically just a label that we can stick to a Pokémon to describe what it is. We can compare type by what the label says. However, unlike the Pokémon themselves a type is just a simple value. If you change it then you're not updating the original type, you are removing it and adding a completely new one.

For example, If we evolve an Eevee we are not changing the "Normal" type, we are pulling of its type label and replacing it with a new one altogether depending on the stone we use. Every other Eevee will still remain a "Normal" type because we haven't changed what "Normal" is we have just replaced it with a new one. There is no inherent connection between the two.

In programming this type of value is called a Primitive type. It's a value that cannot be changed, only replaced. In Javascript this includes numbers, strings, and booleans. For example, When we update a boolean variable, we are not editing the concept of true or false, we are just updating the variables value to be the same as the given value (sticking a new label on with a new value). We call these types immutable (because unlike reference types they cant be mutated). You can not change what 5, true or "hello world" is. If you modify a variable holding an immutable value then the underlying value is not changed, just the variable itself. This is important because otherwise we might accidentally do something like 5=6 or true=false which would be very confusing.

const firstEeveeType = "Normal";
let secondEeveeType = firstEeveeType
secondEeveeType = "Fire";
console.log(firstEeveeType); // Normal
console.log(secondEeveeType); // Fire

We can see from the code above that setting one primitive type equal to another does not tie the two together. Each one is its own separate value and changing one does not affect the other. When we set the secondEeveeType to match firstEeveeType we are copying over the value. When we then change it we are not updating "Normal" but just switching a variable to hold a completely new value.

let eeveeType = "Normal";

const useFireStone = (type)=> {
        type = "Fire";
}
useFireStone(eeveeType);
console.log(eeveeType); // Normal

We can see this mechanic again but this time copying a primitive value into a parameter. When we pass eeveeType into useFirestone we are creating a new label and copying the value of the text onto it. That means when we change this new label to "Fire" it has no effect on the other labels with the same value.

The duplication glitch

If you believe my classmates when the first games came out there is a myriad of different ways to duplicate your Pokémon. As we know now though changing a Pokémon's attributes doesn't work to create a new one, so what do we do?

In the games it involves some precise inputs and timings... luckily in Javascript it's much simpler.

const myFirstPokemon = {
    name: "Pikachu",
    level: 99,
}
const mySecondPokemon = structuredClone(myFirstPokemon);
mySecondPokemon.nickName = "Sparky";
console.log(myFirstPokemon.nickName); // undefined

structuredClone is a relatively new function that's supported by all browsers, allowing you to duplicate a copy of a reference type. We can see, unlike the example before, structured clone is allowing us to create a whole new object with the same attributes as the original.

What if im using a previous version of Javascript?

If you don't have access to structuredClone then your stuck with the older workaround: JSON.parse(JSON.stringify(myFirstPokemon)).

We hope to see you again

There is plenty more to get stuck into with types and mutation. However, this should cover the basics of what we need to know for the next time when we tackle the next episode of Reactémon, where we'll be looking at why we should always use keys for our poké balls!