react-native, extended stylesheet and bootstrap

Learning how to use styles with react-native

I’ve learned that in order to apply styles to react-native, I would use StyleSheet as such:

// src/components/Header.jsx

import React from 'react';
import { StyleSheet, View, Text } from 'react-native';

function Header() {
  return (
    <View>
      <Text style={styles.heading}>Heading</Text>
    </View>
  )
}

const styles = StyleSheet.create({
  heading: {
    fontSize: 20,
    textAlign: 'center'
  }
})

In keeping with my goal of reusing as much of the existing app as possible, I want to use Bootstrap for basic styles. This can be achieved with StyleSheet.compose. (I’m also splitting the styles into its own module.)

// src/components/Header.jsx

import React from 'react';
import { View, Text } from 'react-native';
import styles from './Header.style.js';

function Header() {
  return (
    <View>
      <Text style={styles.heading}>Heading</Text>
    </View>
  )
}

// src/components/Header.style.js
import { StyleSheet } from 'react-native';
import BootstrapStyleSheet from 'react-native-bootstrap-styles';

const { bs } = new BootsrtapStyleSheet();

const styles = StyleSheet.create({
  heading: {
    fontSize: 20,
    textAlign: 'center'
  }
});
export default {
  ...styles,
  heading: StyleSheet.compose(bs.s1, styles.heading)
}

So far, it’s pretty straightforward.

Next, I want to use CSS features such as rem units and custom properties, in order to keep the native design as consistent with the web design as possible. From some searching around, the way to use those features is with react-native-extended-stylesheet. A simple example of its usage would look like so:

// src/components/Header.jsx
import React from 'react';
import { View, Text } from 'react-native';
import EStyleSheet from 'react-native-extended-stylesheet';

function Header() {
  return (
    <View>
      <Text style={styles.heading}>Heading</Text>
    </View>
  )
}

const styles = EStyleSheet.create({
  heading: {
    fontSize: '1.5rem',
    color: '$colorGreen'
  }
});

// src/index.jsx

import EStyleSheet from 'react-native-extended-stylesheet';

EStyleSheet.build({
  $rem: 16,
  $colorGreen: '#4caf50'
});

The EStyleSheet.build() is important to to define the variables, as well as “calculate” the stylesheets. However, because of that step, we can’t use EStyleSheet as a drop-in replacement for StyleSheet when it comes to style composition. At import time, the styles have not been “calculated” yet.

// This does NOT work

// src/components/Header.style.js
import { StyleSheet } from 'react-native';
import EStyleSheet from 'react-native-extended-stylesheet';
import BootstrapStyleSheet from 'react-native-bootstrap-styles';

const { bs } = new BootsrtapStyleSheet();

const styles = EStyleSheet.create({
  heading: {
    fontSize: '1.5rem',
    color: '$colorGreen'
  }
});
export default {
  ...styles,
  heading: StyleSheet.compose(bs.s1, styles.heading)
}

In order to combine extended stylesheet with bootstrap styles, the styles need to be dynamically generated at component runtime:

// src/components/Header.jsx

import React from 'react'
import { View, Text } from 'react-native';
import getStyles from './Header.style.js';

function Header() {
  const styles = getStyles();
  return (
    <View>
      <Text style={styles.heading}>Heading</Text>
    </View>
  )
}

// src/components/Header.style.js

import { StyleSheet } from 'react-native';
import EStyleSheet from 'react-native-extended-stylesheet';
import BootstrapStyleSheet from 'react-native-bootstrap-styles';

const { bs } = new BootstrapStyleSheet();

const styles = EStyleSheet.create({
  heading: {
    fontSize: '1.5rem',
    color: '$colorGreen'
  }
});

export default function () {
  return {
    ...styles,
    heading: StyleSheet.compose(bs.h1, styles.heading)
  }
}

There is a bit more wrapping layers in this workaround, but it achieves the goal of keeping the styles consistent with the usage of Bootstrap and CSS features.

Learning react-native

I am adding an Android native version to a React app I’m maintaining as an attempt to learn react-native. I currently know next to nothing about react-native or native development, even though two of my closest friends are native and react-native developers.

Hopefully this exercise will provide me with an opportunity to be more informed about building a user experience that feels more native. As a strong believer in the web, I am a little apprehensive about this endeavor. I suspect there will be concepts that I find “weird” or difficult to wrap my head around, especially around CSS and layouts.

I also worry that this will add a major burden of maintenance for this app. Every new change will need to be done in the web version first, and require another pass to make it work the same for native. I am aware of react-native-web, but I doubt it is something I would consider seriously. I still very much enjoy using “native” web constructs.

Going into this exercise, I have a couple of high-level goals.

Not knowing much about react-native, I am not sure if these are realistic. At this moment, I am quite insistent on the first goal, as it would be “the whole point” of react-native for me. The elusive idea of “write once, run everywhere”. Granted, much of the component markup/ CSS would not be reusable. I am hoping that the redux stuff, such as reducers and actions, or selectors, could be used wholesale. They contain a significant portion of the “business logic”, and it would be hugely beneficial to not have to rewrite them. There are logic embedded in the components as well, though it’s unclear to me whether they can be shared between web and native at this point.

The second goal is really about reducing the aforementioned maintenance burden. The more similar/ in parallel the web and native code are, the easier it would be to implement features for both.

I will be documenting weird hacks and mistakes along the way. There will be plenty.

Fun with Auth0, React and tokens

I am working on a React-based Single-Page App (SPA) using Auth0 for authentication. For a while, I have been maintaining my own little helper library to do this. It uses the auth0/auth0-js library (which is described as “Client Side JavaScript toolkit for Auth0 API”) under the hood.

I’ve been aware of a new-ish recommended way to work with Auth0 for a while, and finally decided to migrate my sloppy integration over. I first started with auth0/auth0-spa-js, but soon realized that Auth0 maintains a React SDK, which uses auth0-spa-js under the hood, so I went with auth0/auth0-react instead.

Most of the steps are pretty straightforward as documented in this quick start guide. The part I struggled with the most was getting the Access Token to call a backend API.

Before, in my sloppy integration, I was using the ID token returned from the OAuth flow to call my backend API. It is actually a bad/ wrong implementation, as called out by Auth0 tokens documentation:

Do not use ID tokens to gain access to an API.

So in order to get a proper access token, I have the following code in the App.jsx component.

import React, { useState, useEffect } from 'react';
import { useAuth0 } from '@auth0/auth0-react';

function App() {
  const [token, setToken] = useState();
  const { getAccessTokenSilently } = useAuth0();

  useEffect(() => {
    (async () => {
      const accessToken = await getAccessTokenSilently();
      setToken(accessToken);
    })();
  });
}

However, I could not understand why the accessToken I received was not in JWT format, but a small string of maybe 15 characters. It wasn’t a Bearer token I could use to make XHR call against the REST API server.

The Auth0 doc page on Access Tokens mentions 2 different kinds – “opaque” and “JWT”, but it didn’t mention how to get the JWT token. I was guessing that the small token I got back was the opaque token.

Not able to find any reference to this issue online, I randomly tried adding the audience to the call to get the token, which turned out to be exactly the missing piece for me.

  const token = await getAccessTokensSilently({
    audience: 'https://myapi.com'
  })

In hindsight, I’m guessing I was getting the opaque token because by default, Auth0 was returning the access token needed for the /userinfo endpoint only. If anyone knows the answer to this, I would love to know.

UPDATE: This behavior is documented in this post https://community.auth0.com/t/why-is-my-access-token-not-a-jwt-opaque-token/31028

In order to receive a JWT you must include an audience parameter with your token request.

“Between the World and Me” – Ta-Nehisi Coates

I’ve been wanting to read this book for a long time, and finally found the time to do so at the end of 2020. Wow! What an insightful read! I’m glad I got to experience it.

Based on reviews and comments about this popular book, one might go into reading it expecting an intellectual conversation about the history of race and racism in America. I was however enthralled by an intensely emotional and honest telling of a personal narrative. Even though the story is of an individual, I couldn’t help but think about the countless human stories similar to this one, having been lived and is being lived out every day, some perhaps more tragic. I also found myself comparing the story with how I grew up and found my ways in the world, and how different my experiences and thus perspective are. Yet there is a common humanity in the personal struggles and the human emotions that I could relate to and recognize. That is the lesson I took away from the book – while the conversation about race and racism is often explored through the lens of rationality and morality, it is first and foremost a lived experience, and one filled with intense and personal feelings and memories.