How to create a 'plug'n'play' package using React

Aug 06, 2019
hackajob Staff

Article by Andrico Karoulla

Growing up, I spent my spare time doing what most programmers did; playing video games at every waking hour. In particular, I loved Adventure Games and how time just seemed to vanish away. If time was the Mary Rose, and I played the role of the French, my artillery were games such as Kingdom Hearts, Ōkami, and Borderlands.

Why did myself and my friends spend so much of our spare time exploring, surviving, dying, and (so, so much) grinding? What is it that makes the very notion of video games so appealing? Hundreds of factors contribute toward making an engaging experience, but the one I'm going to focus on is the notion of progression.

The idea of gamification isn't new, with many popular applications (like todoist, or challenge timer) incorporating some sort of progression scheme to make us use their app, give them money, and hand over our personal data. Thinking about this and the simplicity behind how it all works, I decided to try my hand of enabling others to do just this; via beautiful skill trees! Please note that I do not expect neither money or data from those using the skill trees in this tutorial.

With the above in mind, I decided to create what I hope to be a pleasant 'plug'n'play' React package to help you create exciting skill trees. You'll be able to test it out for yourself by following the tutorial and hopefully, it'll be a frictionless experience.

We're aiming to create something that will look like the below:

Grab the starter repo by using git clone git@github.com:andrico1234/borderlands-skill-tree.git

Move into the directory and run the starting script, yarn start. Make sure to give the site a try - you should be able to see nothing other than the Borderlands logo and general environment.

beautiful-skill-tree exposes three components: the SkillProvider, SkillTreeGroup, and the SkillTree components.

SkillProvider: This takes in no props and supplies the children with the skill tree's context. This will handle all of the global data related to the skill tree.

SkillTreeGroup: This is more involved, meaning that it can take an optional theme property, where we can pass in some custom styling and make our skill tree have a 'Borderlands feel'. The SkillTreeGroup also uses the children-as-a-function pattern to give us access to some imperative api functionality, such as skill tree reset, selected skills counter and more. Don't worry, we won't need to think about any of those for the scope of this article.

SkillTree: This is the most exciting of the package's exports, unless you're a sucker for typings (which are also exported, for all you TS fans). The SkillTree doesn't take any children but requires 3 props: treeId, title, and data. The treeId should be an id that's unique to each skill tree, but should be persistent across user sessions as this is used as the key for getting and setting the data to local storage. I'm not going to explain what the title prop does and will instead leave you to experiment. The data is the mixing pot of the application. You'll pass in your skill tree data structure which the app will use to render a beautiful-skill-tree.  Let's get a basic tree going before we move on to our multi-tree, multi-branch Borderlands spectacular.

In App.tsx, import the 3 components like so:

import { SkillProvider, SkillTreeGroup, SkillTree } from 'beautiful-skill-tree';

Place it underneath your img tag, outside of the image's container div, but within the outer div. Add the SkillProvider, passing the SkillTreeGroup as a child. Before you do the same with the SkillTree, remember that as SkillTreeGroup uses function-as-a-child pattern, you'll need to render a function that returns the child components. Return a single SkillTree and give it a treeId and a title prop. Pass an empty array into the data prop so your App.tsx looks like this.

function App() {
  return (
    // <div>
    //  <div headercontent />
        <SkillProvider>
          <SkillTreeGroup>
            {() => {
              return (
                <SkillTree treeId="basic-birch" title="First Skill Tree" data={[]} />
              )
            }}
          </SkillTreeGroup>
        </SkillProvider>
    // </div>
  );
}

Go to localhost:3000 to see the application running. You should see the logo, background, and a grey rectangle. If you're running into any errors, go through the introduction again and check to see if there any syntax error or incorrect imports.

Next, let's create a real basic tree, just 3 items that move in a linear line. The data structure looks like this:

type Skill = { 
    id: string;
    icon?: string;
    title: string;
    tooltip: {
      description : string;
    },
    children: Skill[];
}

Each skill requires four properties, with one being optional. You should also notice that the children property is a recursive type, meaning that it takes an array of the same data structure, which it uses to render the children of the skill. This can go on infinitely, and make for some complicated, winding trees. I'll create the first skill for you, and you can then carry on for the next two items.

const data = [
  {
    id: 'first-skill',
    title: 'The root node',
    tooltip: {
      description : "The parent node, all of the descendants will be locked until it's selected",
    }
    children: [
      // rinse and repeat; always repeat.
    ]
  } 

Add the above snippet to the App.tsx file, and replace the empty array inside of the SkillTree's data property with our data definition. Load your page, and you should have an interactive node. Give it a hover and a click and it should react to your actions. If things are working, then you can create two (or more) child nodes. Experiment with children and sibling lengths, to see what you can come up with. (Don't worry if you accidentally happen to break anything, leave me a GitHub issue and I'll patch things up).

Once comfortable with creating a skill tree, let's go ahead and create our Borderlands skill tree. Fortunately, the work's already been done for you and I've created the data structures, as well as accumulated the images.

You'll need to import the three trees from the data file, which can be done via:

import { motion, harmony, cataclysm } from "./data/data";

The next step is creating two additional SkillTrees alongside the current one. You'll need to wrap them in a React.Fragment as your SkillTreeGroup will now be trying to render 3 top level components. Pass in the data accordingly, and if you're unsure, I've posted the code snippet below.

<React.Fragment>
  <SkillTree treeId="motion" title="Motion" data={motion} />
  <SkillTree treeId="harmony" title="Harmony" data={harmony} />
  <SkillTree treeId="cataclysm" title="Cataclysm" data={cataclysm} />
</React.Fragment>

Go ahead and check your web browser, as the work should be almost ready. We've got the skills rendered, but look at the styling. Doesn't it feel a little lacklustre? It certainly doesn't feel very 'Borderlands'. Fortunately, I'm a regular Neil Buchanan and prepared a custom theme. Import the theme and pass it through to the SkillTreeGroup's theme prop. The theme object is export via import theme from './data/theme';. Easy!

Once you've done the above, check out the finished product. If you're still not satisfied with the styles, be sure to look at the theme object and customise it yourself. There are a bunch of additional attributes whose styles can be adjusted, so just peek into the typings of the package.

I mentioned earlier that there are a few additional properties and values that can be used to tweak the skill tree, so be sure to have a play around, and link us to any cool trees you create. I'd love to add it to the growing list of trees found here.

I hope you've enjoyed tinkering with the beautiful-skill-tree package. I'm always adding new features and updating, so give it a star on github! You can find an online demo of the borderlands skill tree here.