React - Everything I needed to know to build my first mobile app

A collection of useful notes about React. I created these notes for my own reference during the development of my first React Native app, but I hope someone else might find them to be helpful.

Having a basic understanding of how React works before diving deep into React Native and mobile app development is essential in my opinion. This article won't teach you React if you don't know anything about it, but it will provide a brief overview of the most important things you need to know.

1. Component structure

React components are very much like JavaScript functions. They accept some inputs and return output. Inputs are so-called props (short for properties) and outputs are React elements (more on this later) that describe how the component should be displayed.

To write a component you can use regular functions:

function Document(props) {
  return (
    <div>
      <p>{props.title}</p>
      <p>{props.description}</p>
    </div>
  );
}

or arrow functions: 

const Document = (props) => {
  return (
    <div>
      <p>{props.title}</p>
      <p>{props.description}</p>
    </div>
  );
};

To avoid using dot notation {props.title} and {props.description} you can destructure the props object like this:

const Document = ({title, description}) => {
  return (
    <div>
      <p>{title}</p>
      <p>{description}</p>
    </div>
  );
};

One important rule to remember is that you should never ever modify props! React is very flexible in many ways but this is a strict rule. If you try to modify props React will throw an error like this: Uncaught TypeError: Cannot assign to read only property. Just remember that props are read-only and are used to pass data from one component to other components.

2. State

Unlike props, the state is not read-only and it can be updated — but only from inside the component! The state of a component is used to store data about the component itself and it can't be accessed or updated outside of the component. It's just like a local variable inside a function.

Modern React uses hooks, so to declare a state variable we have to use the useState hook:

import React, { useState } from "react";

function People() {
  const [peopleData, setPeopleData] = useState([]);
...
...

The useState hook returns an array with two elements, where the first value is the current state and the second is a function that is used to update the state (setter function). The initial value for the state variable is provided as the param of the useState hook and in our case, that's an empty array.

React will re-render components automatically whenever there is a change in their state. State change will also trigger the re-render of all child components! On the other hand, state changes in child components won't trigger the re-render of the parent component.

Useful links: Using the State Hook

3. Fetching remote data

Fetching remote data from an API endpoint is a very common task. How to get data from an API? Use a combination of Axios and the useEffect hook.

Axios is an HTTP client, and to use it you first have to install it with npm (npm install axios) or any other package manager.

In short, the useEffect hook allows you to perform side effects in your component. Side effects must be separated from the rendering process of your component because they are not predictable. And they aren't predictable because you are interacting with the world outside of the component. For example, your API call might fail and instead of some data, you will get an error response. So, this hook is performing a side effect AFTER the component renders.

This might sound complicated, so for now just remember that you can use this hook to fetch some API data or to update DOM.

Now, let's grab some data from The Star Wars API and display it in a list:

import React, { useEffect, useState } from "react";
import axios from "axios";

function People() {
  const [loading, setLoading] = useState(false);
  const [peopleData, setPeopleData] = useState([]);

  useEffect(() => {
    fetchPeopleData();
  }, []);

  const fetchPeopleData = async () => {
    setLoading(true);
    try {
      const { data } = await axios.get("https://swapi.dev/api/people");
      setPeopleData(data.results);
    } catch (error) {
      console.error(error.response.data);
    } finally {
      setLoading(false);
    }
  };

  if (loading) {
    return <div>Fetching data...</div>;
  }

  return (
    <ul>
      {peopleData.map((item, index) => (
        <li key={index}>{item.name}</li>
      ))}
    </ul>
  );
}

function App() {
  return (
    <div>
      <People />
    </div>
  );
}

export default App;

The useEffect hook tells our component that when it mounts, it should call the fetchPeopleData() function to populate the peopleData array. While we are loading data, our users will see the "Fetching data..." message.

The second parameter of the useEffect hook is an empty array. This means that fetchPeopleData() function will be called only when our component mounts for the first time. On subsequent updates, it won't be called, so this basically prevents an infinite loop. If your effects depend on some value, like on a prop then add that prop to the array as a dependency.

Useful links: Using the Effect Hook and What are dependency arrays in React?

4. Conditional rendering

If operator

You can use the If operator for early returning and this is very useful in situations where you are loading some data and you want to show a Spinner component or just some text to your users.

if (loading) {
  return <div>Loading data...</div>
}

return (
  <div>Data loaded</div>
);

And (&&) operator

Use this approach only if the condition evaluates to a boolean value (true or false). If the condition doesn't evaluate to a boolean, then it may cause issues and in that case, use the ternary operator.

function MyCoolComponent({name}) {
  return (
    <div>
      <h1>Title</h1>
      {name === 'Goran' && <MyConditionalComponent />}
    </div>
  );
}

Just a note that this works because the logical AND expression is a short-circuit operator. In other words, if the first operand is equal to false then Javascript won't evaluate the second condition.

Ternary operator

Using this approach will prevent some bugs that may appear if you use the AND operator and you have a non-boolean condition.

function MyCoolComponent({condition}) {
  return (
    <div>
      <h1>Title</h1>
      {condition ? <MyConditionalComponent /> : null}
    </div>
  );
}

5. React elements

Take a look at the following component:

const Document = ({title}) => {
  return (
    <div>
      <p>{title}</p>
    </div>
  );
};

As you can see the component returns something that looks like HTML. But it's not. It's called JSX and it's used to describe what the user interface should look like. JSX produces React elements which are plain immutable objects representing the user interface at a certain point in time.

To print a constant or a variable use the curly braces. That will tell the JSX parser to interpret the contents inside them as JavaScript instead of plain string.

import React from 'react';

const Footer = () => {
  const currentYear = new Date().getFullYear();

  return (
    <View>
      <Text>© {currentYear} My company name.</Text>
    </View>
  );
};

export default Footer;

You can put any JavaScript code inside curly braces in JSX, so in a way, you might say that JSX is a template language that uses HTML and JavaScript.

function prefixName(prefix, name) {
  return prefix + ". " + name;
}

const Document = ({ name }) => {
  return (
    <div>
      <p>{prefixName("Mr", name)}</p>
    </div>
  );
};

function App() {
  return <Document name="Goran" />;
}

export default App;

Useful links: Introducing JSX

6. Custom hooks

Looking at the code in section #3 you might notice that we have the fetching logic inside a component that might be called a "presentational component". Presentational components (or UI components) are all about how things look (in our case it displays a list of people), so it probably makes sense to move that logic to another component or to be more precise to a custom hook.

Custom hooks are a great solution for extracting component logic into reusable functions. For example, you may want to have a different component that will display the list of people in a table, so to keep our code D.R.Y (don't repeat yourself) keeping the fetching logic outside of the presentational components is a no-brainer.

Just like built-in hooks, custom hooks also start with the prefix "use". Here's our custom hook (located in hooks/useFetchPeopleData.js):

import { useEffect, useState} from 'react';
import axios from 'axios';

const useFetchPeopleData = () => {
  const [loading, setLoading] = useState(false);
  const [peopleData, setPeopleData] = useState([]);

  useEffect(() => {
    fetchPeopleData();
  }, []);

  const fetchPeopleData = async () => {
    setLoading(true);
    try {
      const { data } = await axios.get("https://swapi.dev/api/people");
      setPeopleData(data.results);
    } catch (error) {
      console.error(error.response.data);
    } finally {
      setLoading(false);
    }
  };

  return [
    loading,
    peopleData,
  ];
};

export default useFetchPeopleData;

Finally, we can refactor our initial component, remove the fetching logic and start using our new custom hook:

import React from "react";
import useFetchPeopleData from "./hooks/useFetchPeopleData.js";

function People() {
  const [loading, peopleData] = useFetchPeopleData();

  if (loading) {
    return <div>Fetching data...</div>;
  }

  return (
    <ul>
      {peopleData.map((item, index) => (
        <li key={index}>{item.name}</li>
      ))}
    </ul>
  );
}

function App() {
  return (
    <div>
      <People />
    </div>
  );
}

export default App;

Useful links: Building Your Own Hooks and Rules of Hooks

About the Author

Goran Nikolovski is an experienced web and AI developer skilled in Drupal, React, and React Native. He founded this website and enjoys sharing his knowledge.