DEV Community

Cover image for React Native vs. React: Basic Syntax
Daniel Wagener
Daniel Wagener

Posted on

React Native vs. React: Basic Syntax

I've just started taking Stephen Grider's React Native course, and he explains a lot of React and JSX concepts to people who might not be familiar with them. If you're like me and already know React, read on for just the info specific to React Native.

React Native Elements - The First Three

Let's have a look at this React Native Component:

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

const ComponentsScreen = () => {
  const name = 'Daniel';
  return (
    <View>
      <Text style={styles.headingStyle}>Getting started with React Native</Text>
      <Text style={styles.greetingStyle}>My name is {name}</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  headingStyle: {
    fontSize: 45
  },
  greetingStyle: {
    fontSize: 20
  }
});

export default ComponentsScreen;

A few new things here! Let's break down that React Native import statement.

Text

Any time we want to show some text to the user, we have to wrap in in a Text component. Any text we try to display outside of this component will result in an error.

StyleSheet

To give our elements CSS styling, we create style objects (just like we would in React), put them all into a bigger object, and then pass that object into StyleSheet.create(). You might be thinking, "Why can't we just define styles inline?" The good news is, we can! However, if we define a non-React-friendly property (like fontsize), StyleSheet.create() will catch it and throw an error for us. We miss out on this check if we define styles inline.

View

If we want to render multiple JSX elements, we have to wrap them in a View component. In React we could have just used div tags, but those would result in an error in React Native.

FlatLists

When we want to render a list of JSX elements from an array in React, we're used to using a map method. In React Native, we have to switch gears and make use of an element called FlatList.

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

const ListScreen = () => {
  const friends = [
    { name: "Friend A" },
    { name: "Friend B "},
    { name: "Friend 4" },
  ]
  return <FlatList data={friends} renderItem={element => {
    //some code here
  }}/>;
};

const styles = StyleSheet.create({});

export default ListScreen;

Looking at this element, the data attribute is easy enough to grok, but what about the renderItem attribute? Is that the same as a map function? Unfortunately, no. In the first iteration of renderItem, element would look like this:

{
   item: { name: "Friend A" },
   index: 0
}

If we're only interested in the item value, we can use a bit of ES6 destructuring:

const ListScreen = () => {
  const friends = [
    { name: 'Friend A' },
    { name: 'Friend B ' },
    { name: 'Friend 4' }
  ];
  return (
    <FlatList
      data={friends}
      renderItem={({ item }) => {
        return <Text>{item.name}</Text>;
      }}
    />
  );
};

Of course, just like in React, we have to define a key attribute every time we render a list. If we don't feel like declaring a key property for every item in our friends array, we can take advantage of React Native's keyExtractor method:

return (
    <FlatList
      keyExtractor={friend => friend.name}
      data={friends}
      renderItem={({ item }) => {
        return <Text style={styles.textStyle}>{item.name}</Text>;
      }}
    />
  );

I know: "Ughhh, why can't we just use the map method?" Well, the upshot of FlatList is that we can add a couple of attributes to easily turn the list horizontal (think Netflix tiles!). Check it out:

return (
    <FlatList
      horizontal
      showsHorizontalScrollIndicator={false}
      keyExtractor={friend => friend.name}
      data={friends}
      renderItem={({ item }) => {
        return <Text style={styles.textStyle}>{item.name}</Text>;
      }}
    />
  );

Buttons

In the beginning of React Native, a component called TouchableOpacity was the sole equivalent of a button. It's powerful and allows for a lot of customization. However, it's not super intuitive, and a lot of developers starting out with React Native got hung up looking for a Button component, so the React Native team added one. It's essentially a simpler version of TouchableOpacity.

Button components

A React Native Button component, unlike an HTML button, is a self-closing element. To render text inside it, we pass in a string as a title attribute.

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

const HomeScreen = () => {
  return (
    <View>
      <Text style={styles.text}>Hi there!</Text>
      <Button title='Go to Components Demo' />
    </View>
  );
};

A nice benefit of Button components is that they render with some styling right out of the box: blue text on iOS or white text on a blue background on Android.

TouchableOpacity

A TouchableOpacity, on the other hand, comes with no styling out of the box, save for a momentary fade-out effect when it is pressed. To make things confusing, TouchableOpacity is NOT a self closing component, and we have to render some other element or elements between the tags.

import React from "react";
import { View, Text, Button, TouchableOpacity } from "react-native";

const HomeScreen = () => {
  return (
    <View>
      <Text style={styles.text}>Hi there!</Text>
      <Button title='Go to Components Demo' />

      <TouchableOpacity>
        <Text>Go to List Demo</Text>
      </TouchableOpacity>
    </View>
  );
};

Adding functionality

In React, we're used to giving buttons an onClick attribute with a callback function. We don't have a mouse to click with when we're using our phones, so the React Native equivalent is called onPress. Here's what that might look like using the navigation method from react-navigation-stack (specifics later in the article!):

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

const HomeScreen = ({ navigation }) => {
  return (
    <View>
      <Text style={styles.text}>Hi there!</Text>
      <Button
        onPress={() => navigation.navigate('Components')}
        title='Go to Components Demo'
      />
      <TouchableOpacity onPress={() => navigation.navigate('List')}>
        <Text>Go to List Demo</Text>
      </TouchableOpacity>
    </View>
  );
};

Images

React Native also has a primitive element for images called Image. To render a local image, we pass the relative path into a require() function and assign it to the source attribute. Be careful: this is the full word source, not src!

import React from "react";
import { View, Text, Image } from "react-native";

const ImageDetail = props => {
  return (
    <View>
      <Image source={require('../../assets/beach.jpg')} />
      <Text>{props.title}</Text>
    </View>
  );
};

One catch is that source has to be a static value. That means can’t write <Image source={require(props.img)} /> — instead, we’d have to pass down the entire require('../../assets.beach.jpg') function call as a prop.

Inputs

Adding a text input element seems pretty easy at first glance:

import React from "react";
import { View, TextInput } from "react-native";

const TextScreen = () => {
  return (
    <View>
      <TextInput />
    </View>
  );
};

However, if we run this file as is, it’ll look like a blank screen. The TextInput is actually there and we can interact with it, but it has zero default styling: no borders, no background, nothing. Let’s take care of that now:

const TextScreen = () => {
  return (
    <View>
      <TextInput style={styles.input} />
    </View>
  );
};

const styles = StyleSheet.create({
  input: {
    margin: 15,
    borderColor: "black",
    borderWidth: 1
  }
});

In addition, phones have auto-capitalize and autocorrect features we might not want to apply to our input. They’re both easy enough to disable:

const TextScreen = () => {
  return (
    <View>
      <TextInput
        style={styles.input}
        autoCapitalize='none'
        autoCorrect={false}
      />
    </View>
  );
};

You might’ve expected autoCapitalize to be a boolean, but it’s not because we actually have a few capitalization schemes to choose from ( sentences, characters, words).
Next, we’re going to want to make this input a controlled input. In other words, we want to connect the input’s value and the component’s state, just like in React. In React, we have an event listener attribute called onChange and we set the state equal to event.target.value. In React Native, that listener is called onChangeText and simply receives a newValue as a parameter instead of an entire event object.

const TextScreen = () => {
  const [name, setName] = useState("");

  return (
    <View>
      <Text>Enter name:</Text>
      <TextInput
        style={styles.input}
        autoCapitalize='none'
        autoCorrect={false}
        value={name}
        onChangeText={newValue => setName(newValue)}
      />
      <Text>My name is: {name}</Text>
    </View>
  );
};

One last note: the React Native equivalent of onSubmit for text inputs is onEndEditing.

Navigation with react-navigation

I originally wasn’t going to talk about specific libraries in this article, but I figured anyone reading this would probably be wondering about it.

The react-navigation library is now in v5 with breaking changes, but yarn add react-navigation installed v4 when I ran it. Apparently v5 has been released but is still in beta or something. The react-navigation has documentation for upgrading to v5 if you’d like. Anywho, assuming you’ve created a React Native app with the Expo CLI, you can run this command to get some helper libraries:

expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view

Also, we’ll need to install these:

yarn add react-navigation-stack @react-native-community/masked-view

Now we can get coding! First, we write a couple of imports:

import { createAppContainer } from "react-navigation";
import { createStackNavigator } from "react-navigation-stack";

Note that createStackNavigator used to be in react-navigation, but now we have to import it from react-navigation-stack.

To get started, we declare a constant called navigator (by convention) and assign it a createStackNavigator call. This function takes two arguments, both objects: the first lists all our route names with their respective components, and the second defines other options.

Let’s tackle that first object argument. Assume we create a component called SearchScreen. To make our app open to the search screen, we import it into App.js and assign it to some (relevant) key in our object:

{
   Search: SearchScreen
}

Then, to make our app open up to SearchScreen when it launches, we specify so in the second object. We can define other things as well, like the title we’d like in our header.

{
   initialRouteName: "Search",
   defaultNavigationOptions: {
      title: "Business Search"
   }
}

Finally, we export our whole component by passing it in to createAppContainer. The whole App.js file would like this:

import { createAppContainer } from "react-navigation";
import { createStackNavigator } from "react-navigation-stack";
import SearchScreen from "./src/screens/SearchScreen";

const navigator = createStackNavigator(
  {
    Search: SearchScreen
  },
  {
    initialRouteName: "Search",
    defaultNavigationOptions: {
      title: "Business Search"
    }
  }
);

export default createAppContainer(navigator);

So now how do we actually perform navigation? Easy enough: our child components are now going to receive navigation as a prop, and that prop contains a navigate method. If we wanted to navigate back to SearchScreen, we’d simply pass the string 'Search' into navigation.navigate(). Hopefully this example from earlier makes more sense now:

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

const HomeScreen = ({ navigation }) => {
  return (
    <View>
      <Text style={styles.text}>Hi there!</Text>
      <Button
        onPress={() => navigation.navigate('Components')}
        title='Go to Components Demo'
      />
      <TouchableOpacity onPress={() => navigation.navigate('List')}>
        <Text>Go to List Demo</Text>
      </TouchableOpacity>
    </View>
  );
};

Hope this helps!

Follow me on LinkedIn, GitHub, and Twitter

Top comments (0)