My Journey From React to React Native
The things that changed for me switching when from web applications to native applications
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>
);
}
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>
);
}
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>
);
}
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>
);
}
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} />
);
}
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>
);
}
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>
);
}
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!