React Native with Hooks and Styled Components

Learn by building a stress ball clicker game

So you want to learn about styled-components?

I’m going to start by talking you through the essential syntax of styled-components then we’ll work our way into more advanced features like using props, variables, and extending styles.

I love learning by building — so we’re going to build a quick stress ball game as we cover the major concepts. It looks like this:

See the Github Repo

Requirements:

  • A Code Editor (I prefer visual studio code)
  • (optional) A syntax highlighter. try this one for VSC
  • NPM/Node
  • Expo
  • Familiarity with React Native and Hooks is recommended— but I will try to explain everything I cover.

Create A React Native Project

Install Expo

npm install -g expo-cli

If this doesn’t work — make sure that you have npm working on your computer by typing npm -v into your terminal. You should see the current version of your npm installation. Go here to install node and npm if you don’t have it installed.

Create a new Project

expo init

From here you have a few options of which template for a new react-native app to use. I recommend picking the blank option.

From here you can name your project whatever you like.

Run the project

You may use a simulator. For the sake of keeping this tutorial to-the-point, I’m going to use expo web.

Go to the terminal and start your project:

expo start --web

Metro Bundler should open in your browser, your project should open in a new tab.

If you don’t see your project click “Run in web browser” in Metro Bunder.

Assuming you’re using Chrome, you can view a react native project as though it were on a mobile device. Other browsers have this ability too — But for the sake of brevity, I’m going to walk through the steps to set it up on Chrome only.

Right-click on the page and click inspect.

Click toggle device toolbar

Then select your preferred device to display.

Install Styled Components

yarn add styled-components

Create your first styled component

In order to use styled-components, you have to import it into the file.

make sure that when you import styled-components you import it from the "styled-components/native" path rather than "styled-components". One is for React Native, the other is for the web. I make this mistake all the time.

Create a Container

Side note, you can also use react native syntax using camelCase rather than hyphening your style attribute names. But you will still have to use units like pixels otherwise your code will break. Because your IntelliSense will work better using hyphens, I recommend sticking to hyphens.

Create the Start Button

So when creating a new component (in this case the green button) it’s useful to think:

“Can this component be broken down into smaller reusable chunks?”

Create BaseButton

In fact, I think it would be really cool to have a button component where I can control both the background color and the text color using props. This means I have one highly reusable component without too much additional complexity.

First, we want to have some default styles for all of our button components. Our buttons have a standard height and minimum width when the text is small. They have some padding to avoid placing the text at the edge of the button. Finally, they have centered text using justify-content and align-items

Create BaseText

I also prefer the look of white text to black text in this case — however rather than making a single WhiteText component or making it so all buttons have white text, we can use a property to control the color of our text.

the following syntax means that if there is a `color` prop, it will be used as the Text color, otherwise, the color attribute will be undefined and default to black.

Create Button

Button also passes color and children props into BaseText.

Extend SuccessButton component with Button

There’s a lot of concepts coming together here so I will break everything down below.

Extending Styles (see documentation)

Attaching Additional Props with .attrs (see documentation)

<Button color={"white"}/>

SuccessButton applies its own background property.

`background-color: ${success};`

Improving Reusability

Render the Start Button

Here’s how your App.js should look. I’m keeping all of the components in the Appfile for the sake of making sure there isn’t any added complexity or issues caused by imports. However, I highly recommend extracting these components into separate files.

And here’s our app

Start the Game

To do this, we’ll use a state variable that controls if the user is playing, starting. Later on this will also handle winning or losing the game.

We’ll build the game later. for now, we just want to render a placeholder for the game to make sure that our button works correctly.

When you click Start you should see the text Game Will Be Here

Create the Game

  1. A clickable StressBall component that when clicked reduces stress level
  2. A StressBar which acts as a gauge for the user’s in-game stress level

Create the StressBar

  1. A StressContainer component that has an outline of the maximum stress level
  2. A StressLevel component inside of the StressContainer that grows and changes color depending on the users stress level.

Create StressContainer

Create StressLevel

Because we’re going to have more properties this time, I’m going to show you a new syntax that is less verbose than using a function in every interpolated string.

Understanding the above block of code requires a bit of understanding of how RGB works in CSS.

rbg takes a numeric value between 0 and 255 for 3 parameters: red, green, and blue. We want our StressLevel component to start at the maximum green value and the minimum red value. Blue is always 0. This is the magic of css-in-js where we can dynamically control styles with javascript.

So when stressLevel is 0 the result is rgb(0, 255, 0). When stressLevel is 255 the result is rgb(255, 0, 0)

Now put everything together and make the StressBar component.

Configure Stress Level with Dynamic Data

Intervals with hooks are a bit finicky. Whenever you use an interval inside of a useEffect hook, and you also use a state value — you have to make sure that your state value is listed as a dependency for the useEffect hook otherwise the value for the interval will be stale and never increase (feel free to remove the dependency and see what happens!)

Whenever you use a timeout or interval inside of useEffect you also have to make sure to clear the interval. (otherwise, this could lead to memory leak and a neverending interval)

If you’re not familiar with the useEffect hook then I recommend reading up on it in the React Docs.

For now, it’s good enough to know that whenever useEffect returns a function, that function will be called with the component unmounts.

This acts a lot like componentWillUnmount if you’re familiar with class components.

we also have to make sure that the stressLevel doesn’t increase beyond the height of its container (255). I’ll use Math.min for this.

Here’s the code:

Now if you run the project you should see that the StressBar rises until it hits the top.

Create the Stress Ball

We’re going to reuse the SuccessButton component because it has the same green background color that we want. And this is going to be a great opportunity to show one of the pitfalls of extending styles, and how to overcome it.

good? err..no

Why is this happening? Remember when we made the Base Button component we defined a min-width assuming that all of our buttons would be rectangular.

We don’t want to remove or alter this property because it would change the styles of any buttons built off of it.

Essentially we’ve coupled all of the button components to this one BaseButton component. Extending styles is powerful and useful but it suffers from the problems of inheritance. You necessarily couple your child components to your parent components.

Now we could simply make a new CircleButton component and that would be a great solution.

But I’m going to take this opportunity to show you how you can override styles when you have to.

Now here’s our corrected StressBall code:

Awesome. Now we can place that inside of our game and here’s our Stress Game so far. Again, I HIGHLY Recommend extracting out components for readability, but I want to make it so you can go to this code gist and copy it directly if you’re having issues.

Create the Reduce Stress FN

Right now, when the user clicks the StressBall component we don’t actually do anything yet.

Let’s make areduceStress function that triggers when the user presses the StressBall.

We have the same problem to solve here that we did where the stressLevel shouldn’t rise above 255, except now we don’t want it to ever be smaller than 0. We can solve this using Math.max

Win or Lose

Add won and lost to GameState

Render the lost and won views based on gameState

When the user loses we want to render a DangerButton (which we will create) that says “You Lost! Try again?”

Create DangerButton

Render won and lost states

Win and lose functions

const winGame = () => setGameState(GameState.won);
const loseGame = () => setGameState(GameState.lost);

Pass them to the Game component

<Game loseGame={loseGame} winGame={winGame} />

Handle winning and losing in Game component

Here’s everything together. I’ve put it all into one file to avoid any complications with imports. If you’re having any trouble getting your code to work feel free to follow the link to this gist. Try copy and pasting it into your App.js to see if it works! You can also clone the repo

Thank you for reading!

Congrats! Provided everything worked correctly for you, you should have a playable game and a better understanding of how to apply styled-components.

If you have any questions please feel free to leave a comment and I will do my best to provide a helpful answer!

If you have any recommendations or notice any mistakes please let me know how you think I can improve! I’m still new to writing and I really appreciate the feedback to help me get better.

Also, if you enjoyed the article I would really appreciate a clap! You can hold down the button if you really liked it!

Software Engineer. I create educational content focused on technology for mobile and web applications.