Build a React Native theme provider using Hooks

A practical introduction to the new feature coming with v0.59

Stefan Wegener
UniNow Engineering

--

Update 12/03/19: The React Native team released version 0.59.

Dan Abramov introduced Hooks at the React Conf 2018 on Oct 26, 2018. When I first saw React Hooks I was very excited about that cool new syntax, but did not see a real use case for our company.

After a while playing around with Hooks in React 16.8 I realized, that Hooks are a great way to write functional components in a very clean way and could replace a lot of boilerplate code in our project.

To get a good understanding of React Hooks, I don’t want to give you an introduction in an abstract “Hello World”-ish manner. Instead, I want to explain Hooks in a practical use case of writing a theme provider for an app including navigation and persistence.

If you haven’t seen Dan’s keynote yet, you should definitely watch it:

Prerequisites

The React Native team released a stable version of 0.59 including hooks. You can init your project with react-native init RNThemeProvider.

To get a navigation-based application we want to use react-navigation as our navigation framework. Since AsyncStorage is no longer part of the React Native framework we have to install it separately. Install all the packages and link the native modules:

Project Structure

Before starting I want to share the project structure with you. This kind of structure I use quite often in my projects and I feel very comfortable with it.

- src 
— components
— TabBar.js # custom bottom tabbar component
— core
— themeProvider.js # custom hook for theming
— themes.json # JSON array containing our themes
— screens
— Main.js # first tab
— Settings.js # second tab
— App.js # navigation part
— index.js # entry point for react-native
— package.json # dependencies

Navigation Part

We start off with the navigation part of our app. Therefore we want to create a TabBar with two tabs. The first tab represents the main content and the second tab represents the settings of our application. To get a TabBar we need to modify the App.js file like that:

For both screens, we write two simple functional components: Main.js and Settings.js, which we will extend later in the UI part.

Now it’s time to run our application. When it looks like this we are done with our first part. 😎

Our application after the navigation part.

UI Part

In the next step we want to extend the settings screen and add a theme chooser to it. For this tutorial, I grabbed some color codes from this page. We define our different themes in a themes.json file like that:

For each theme, we define a key as a unique identifier, a background color, and a text color.

After defining our themes we want to export them inside the themeProvider.js.

We export all themes as an array for the settings page and the second theme as our default theme. We will add logic to this themeProvider later.

In the next step, we extend our Settings.js screen. We add a FlatList to visualize our different themes and use our current theme for the headline.

Also, we update our Main.js screen to use our current theme:

Finally, we add a custom TabBar component to our navigation and use the current theme as the active color. We need to add the TabBar as a navigation option to our App.js file.

Now we are done with the UI part of our application. When we run our application, the app will now look like that:

Our application after the UI Part.

Nothing special yet. Now we will start with Hooks 🎣

Custom Hooks

First of all, we want to use the new useContext Hook to pass the current theme through the component tree. If you are not familiar with the React Context, I recommend to read this.

We need to define the theme context in our themeProvider.js and wrap this context around our application in the App.js file.

To use this context easily, we add a withTheme HOC (Higher Order Component) to our themeProvider.js file. If you not familiar with the HOC pattern, I recommend to read this.

Now we can use this HOC Component in every component that we want to use our theme. We need to update our screens and TabBar like this:

As the next step, we want to change our theme when clicking on one item in the settings screen. To do so we use another React Hook: useState.

We update our theme provider once again to use this hook. Additionally, we use the new setTheme function in our settings screen.

Now our app will change the theme when clicking on a theme item in the settings screen. But it will not persist this state after reload. This will be our last step.

We can now change our theme — but not persisted yet.

To store the selected theme we will use the AsyncStorage. We need to update the setTheme function of our theme provider to store our selected theme.

We need one more step to finish our project. To load our theme when we launch our app we use the useEffect Hook. useEffect can be compared to componentDidMount and componentWillReceiveProps lifecycle functions.

We use this Hook in the ThemeContextProvider to load our theme like this:

Two things you should keep in mind. First: You need to pass an empty array to the useEffect Hook, because we want that the function is called only once. Detailed explanation here. Second: I added a condition check in the render method. Since the AsyncStorage is asynchronously you need to handle the loading state. In this example, our loading screen is just empty, but you can extend this if you like.

Finally, we are done with our project and the state will be persisted over app relaunches. 🎉

The state will now be persisted after app relaunch.

The Takeaway

I think React Hooks are a great way to write complex functional components in a very clean way. I recommend to take some of your existing classes and try to replace them with functional components. You should definitely have a look at it. There are a lot more effect Hooks like useReducer, useRef or useLayoutEffect.

You can find the complete source code here.

We are hiring 🚀. So If you are interested in an app startup using React and React-Native completely for our product join us.

--

--