My Journey From React to React Native

The things that changed for me switching when from web applications to native applications

Photo by Dayne Topkin on Unsplash.
Photo by Mukuko Studio on Unsplash.

I’ve recently started working on an Android application, and as a React developer, I made the easy choice to use and test React Native to do so because it helped me stay in my comfort zone and also gives me the opportunity to explore iOS someday.

Even if it is the same framework, using React for native applications is a little bit different than React on the web.

I’m writing this article to share the main differences I have found between the two platforms along with a few tips I had to figure out to obtain the desired final behavior.

View or Text — There Is No div

When working on a web application, we mostly use div and span tags for many usages. Since we are not on the web, this is no longer a possibility.

Instead, the content is made with View and Text that we could associate with the two tags above, but they have some additional constraints.

The View element

With the View element, you can’t add anything else inside other than components. That means it cannot contain text, which the Text component is for. As an unfortunate consequence, it has a larger tree in your application, but it helps to separate concerns between layout and text.

import React from "react";
import { Text, View } from "react-native";
export default function HelloWorld() {
return (
<View>
<Text>Hello world</Text>
</View>
);
}
view raw HelloWorld.jsx hosted with ❤ by GitHub

Hello world component in React Native

Based on the previous point, you can easily figure out that you can’t apply text-related styles to a View component. The text styles like color or fontSize need to be applied to the Text component.

import React from "react";
import { Text, View } from "react-native";
export default function Styling() {
return (
<View style={{ backgroundColor: "rgb(255, 192, 16)" }}>
<Text style={{ color: "rgba(48, 48, 48)" }}>Hello world</Text>
</View>
);
}
view raw Styling.jsx hosted with ❤ by GitHub

Layout styles on View, text styles on Text

View is also a flexbox container that can only support two display values: none or flex. It can change numerous things if you are not confident with the model, but it is much more powerful than the classic block model used by default on the DOM.

You can learn more about this layout system on CSS-Tricks. Every flex property is supported in React Native, from align-items to flex-grow.

There is, however, one major difference between the web version and the native version: the default value of flex-direction. If we have row on the web, it is set to column in React Native. Basically, this means that elements are placed by default from top to bottom instead of left to right.

import React from "react";
import { Text, View } from "react-native";
export default function Flex() {
return (
<View style={{ flexDirection: "row", alignItems: "center" }}>
<Text>✓</Text>
<Text style={{ flexGrow: 1 }}>Hello world</Text>
</View>
);
}
view raw Flex.jsx hosted with ❤ by GitHub

Flexbox usage on React Native

Finally, View is not clickable. If you need a click behavior on it, you’ll have to wrap it into a Touchable* component:

  • TouchableHighlight to add a background color on click.
  • TouchableOpacity to reduce opacity on click.
  • TouchableWithoutFeedback to have no feedback on click, which I don’t recommend for user experience reasons.
  • TouchableNativeFeedback (only on Android) to have the ripple effect on the button.

import React, { useState } from "react";
import { Text, TouchableHighlight, View } from "react-native";
export default function MyClickableComponent() {
const [clicked, setClicked] = useState(false);
return (
<TouchableHighlight
activeOpacity={0.6}
onPress={() => setClicked(true)}
underlayColor="rgba(0, 0, 0, 0.3)"
>
<View>
<Text>{clicked ? "Click me" : "Clicked"}</Text>
</View>
</TouchableHighlight>
);
}

Example usage of TouchableHighlight

The Text element

If we can easily compare the Text element to a span tag on the web, the difference is as noticeable as with views.

The Text element — as it is aptly named — exists only to make the rendering of text contents. We cannot use it for any layout-related stuff we might need. Therefore, display: "flex" will have no effect. Neither will position.

However, the Text inherits styles from the parent Text component like it does on the web.

import React from "react";
import { Text } from "react-native";
export default function TextInherit() {
return (
<Text style={{ fontSize: 18 }}>
<Text style={{ color: "#0000ff" }}>
<Text>A blue text, sized 18</Text>
</Text>
</Text>
);
}
view raw TextInherit.jsx hosted with ❤ by GitHub

Text component style inheritance

Like View, the Text component is not clickable. If that’s a behavior you need, you will have to wrap into one of the Touchable* components.

Finally, Text is only meant to contain text and other Text components. You should not include layout-related components like View, ScrollView, or FlatList.

Replace Input With TextInput

Since the Native API is not DOM, we do not have input elements either, but React provides a component for the times when we need a form.

The InputField component works the same as input but also has a onChangeText attribute that accepts a callback with the value as an argument. No more need for event.target.value!

import React, { useState } from "react";
import { TextInput } from "react-native";
export default function InputForm() {
const [beerName, setBeerName] = useState("");
return (
<TextInput value={beerName} onChangeText={setBeerName} />
);
}
view raw InputForm.jsx hosted with ❤ by GitHub

TextInput and the onChangeText callback

The CSS Usage

If I’m using CSS Modules when I’m working on a web application, it is a bit different on native, where the CSS usage is more the CSS-in-JS way. The stylesheets are created with the StyleSheet.create method that is provided by React Native and is a key/value object of class/styles for the component.

import React from "react";
import { StyleSheet, Text, View } from "react-native";
const styles = StyleSheet.create({
layout: {
backgroundColor: "rgb(255, 192, 16)"
},
text: {
color: "rgba(48, 48, 48)"
}
});
export default function Styling() {
return (
<View style={styles.layout}>
<Text style={styles.text}>Hello world</Text>
</View>
);
}
view raw StylingSheet.jsx hosted with ❤ by GitHub

Styling with StyleSheet.create()

If there are units in CSS, there are not in React Native — or more precisely, units are always set in dp, so the render will be right even if the phones do not all have the same pixel ratio. It makes the CSS usage a bit different, but if you want to make things simpler, just consider them pixels.

If we used to have shortcuts in CSS, it is not the same in React Native: padding must be a number and not a list of values in a string, backgroundColor is used for the color, and so on.

To illustrate that rule, assume that the CSS padding: "8 16" is not valid, and so background: "#333333".

Even if these are a bit longer to type in, I find it way more explicit than the shortcuts we are used to. Plus, they are always complicated to understand for a beginner.

After a couple of hours, I had definitely adopted this new way of writing CSS.

import React from "react";
import { StyleSheet, Text, View } from "react-native";
const styles = StyleSheet.create({
layout: {
backgroundColor: "rgb(255, 192, 16)",
paddingLeft: 8,
paddingRight: 8,
},
wrapper: {
padding: 16,
},
text: {
color: "rgba(48, 48, 48)"
}
});
export default function Styling() {
return (
<View style={styles.layout}>
<View style={styles.wrapper}>
<Text style={styles.text}>Hello world</Text>
</View>
</View>
);
}

dp units and shortcuts

Scalable Vector Graphics

If SVG is used a lot on the web, it is not natively supported in React Native. We need to use it with an external package: react-native-svg.

However, the package is made to be used exactly like on the web with just a little difference: the first uppercase character.

import React from "react";
import { Svg, Path } from "react-native-svg";
export default function UsingSvg() {
return (
<Svg height="20" width="20" viewBox="0 0 24 24">
<Path d="M4 4 H 20 V 20 H 4 L 4 4" fill="#ffb010" />
</Svg>
);
}
view raw UsingSvg.jsx hosted with ❤ by GitHub

Simple SVG in React Native

Overflow

If you want a scrollable View, you need to switch to the ScrollView component. It acts the same but has a scrollbar built in.

Since the component has a vertical scroll by default, you can use the horizontal attribute to make it scroll on the x-axis.

For performance reasons, you can also use theFlatList component, which is a bit more complicated to use, but it will make your long lists scroll fast. If it is something you need, I encourage you to look at the official documentation.

Tips and Tricks

Touchable components are applied to a single element

If you get the error Error: React.Children.only expected to receive a single React element child, then you just need to wrap your elements in a new View component.

It seems pretty obvious what to do, but it can be a bit disturbing when coming from the web: When using Touchable* components, you need to have a single layout item.

Line breaks in Text

On the web, new lines are made with <br />, but since native is not DOM, you can simply use {"\n"} in your Text components or directly in a string (e.g. <Text>{"Hello\nworld!"}</Text>).

Views in Text

You cannot have View elements in Text elements. This throws the following error: Cannot add a child that doesn't have a YogaNode to a parent without a measure function!.

It might make your tree a bit more complex with some code duplication, but you should always find a way to avoid this message.

Conclusion

Even though React Native is based on React, there are plenty of differences. On one hand, we use React on the web and use the DOM API. On the other hand, we use the native layouts for Android, iOS, and others. But it is still very easy to get into. Do not hesitate to give it a try!