Through this adventure we will see how to create a cookbook.

I won’t loose any time telling you what React Native or NodeJS are. I’m sure there are a lot of medium posts explaining what are these two. However, I’m sure some of you don’t know about NestJS. And through this medium post, I hope you’ll learn something from me !

So let’s get into it !

Init React Native with expo

First of all, what is “expo” ? Expo is somewhat a top layer for React Native, providing librairies and tools to help developers to develop, build & deploy their React Native application.

First of all, make sure you have node installed on your computer with Node’s version higher than 10.0.0. (You can install it here).

Now we need to install expo-cli, that will allows us to create easily a RN project, start it locally and publish it.

$ npm install -g expo-cli

expo-cli while ask you few questions :

  • 1st : choose blank (TypeScript) project
  • 2nd : enter the name of your application
  • 3rd : Type n + enter to not use Yarn (I love npm to much for this)

Note: If the command is blocked while ‘Installing dependencies…’ don’t panic, you can still stop it with Ctrl + C, remove your node_modules and execute npm install.

Now you can go to your React Native project repository (if you forgot how to do it : $ cd cookbook) and run your application in your browser with :

$ npm run web

To run it in your browser you’ll need to have the latest version of expo-cli, so if you have this kind of log: error: unknown option '--web-only' you may need to install the last version.

Navigate to localhost:19006 and you’ll get this. Congratulation, you’ve made the first part !

Sources : https://facebook.github.io/react-native/docs/0.59/getting-started If you have trouble to use Tunnel or Lan options it may come from your firewall or your internet provider. Expo is using ngrok a service creating secure and introspectable tunnels to localhost. But it’s using a port your company policy might have blocked.

Init your server with NestJS

It’s very easy to init a NestJS project, all you have to do is install nest-cli and set up a new project with these only two commands:

$ npm i -g @nestjs/cli
$ nest new project-name

You’ll have to answer 1 question:

  • select ‘npm’ (you know why)

All you have to do now is to run your server:

$ cd project-name
$ npm run start

Then navigate to localhost:3000 and you’ll see a wonderfull ‘Hello World!’ (how unusual) ! Well done you have just completed the second part of the tutorial, you’re the best ! 👍

Source : https://docs.nestjs.com/

Display HTTP response in your React Native App

Well we have one react native application and one server working separately, let’s make them communicate together. All we want to do for now is to display the original message ‘Hello World!’ in our React Native app.

For this we’ll need to update our App.tsx. This file is the entry file for our React Native application processed right after the app.json. We’ll dig these files later, for now let’s focus on our request.

This is how our App.tsx looks like right now

import React from "react";
import { StyleSheet, Text, View } from "react-native";
export default function App() {
  return (
    <View style={styles.container}>
      <Text>Open up App.tsx to start working on your app!</Text>
    </View>
  );
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
});

React like other Javascript frameworks (Angular, VueJS) works with components (read more about Javascript components here). We have what looks like HTML. Actually this is JSX, a syntax for embedding XML within JavaScript. I will talk about his in an other post, but you can still learn more about it here. We also have CSS we apply to the JSX.

Before making our HTTP call we’ll need to dig into the lifecycle of a React component.

React Lifecycle Component

ReactJs lifecycle phases and methods. From this post : ReactJs component lifecycle methods — A deep dive

For this chapter we’ll focus on the Initialization & Mounting steps:

  • 1st : React will set props & states. Props are kind of like @Input in Angular. You pass props from a component to its child, and get them this way :
constructor(props: any) {
  this.state = { isLoading: true};
}

States are used to store data for your current component to do so you’ll have to use this.setState().

  • 2nd : componentWillMount() I’m pretty sure you understand that every Javascript line you insert in this method will be execute before React render the JSX page.
  • 3rd : render() method will display the JSX content of your component. You can add conditions to display a JSX content or an other (we’ll see that later) that’s why you want to wrap your JSX in this method.
  • 4th : at last componentDidMount(), same as 2nd, every line of code inside this method will be executed once you component mounted (after the render).

So first of all, let’s make our class looking more like a React Component. Let’s modify our App.tsx :

export default class App extends React.Component<IMyComponentProps, IMyComponentState> {
 
}

Because we’re using Typescript we need to declare how our prop & state objects look like. So above the declaration of our class we’ll add this :

interface IMyComponentProps {} // we don’t use props for this case.
interface IMyComponentState {
  isLoading: boolean;
  dataSource?: string;
}

Now let’s add our constructor(executed before render()) which is similar to componentWillMount() method and for some obscur reason (react developers know why) we’ll use constructor instead of the other one. I leave you this StackOverflow response here.

constructor(props: any) {
 super(props); // <-- this
 this.state = { isLoading: true};
}

It’s useless to inherit props from parent right now because we are at the root of our application, but seems to be a good practice so we’ll leave it here. In our constructor we set a state value to true, this will be used in our render to display the loading screen waiting for the HTTP response.

render(){
  if(this.state.isLoading){
    return(
      <View style={{flex: 1, padding: 20}}>
        <ActivityIndicator/>
      </View>
    )
  }

  return (
    <View style={styles.container}>
      <Text>{this.state.dataSource}</Text>
    </View>
  );
}

So this is how our render()looks like, until we receive our HTTP response we’ll display a loading element <ActivityIndicator/>. To be able to use it, we need to import this element from React Native :

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

Finally we’ll write our HTTP call in componentDidMount():

componentDidMount(){
  return fetch('http://localhost:3000')
    .then((response) => response.text())
    .then((responseString) => {
      this.setState({
        isLoading: false,
        dataSource: responseString
      });
    })
  .catch((error) =>{
    console.error(error);
  });
}

We use fetch to execute our HTTP request, for this exemple I’m not setting the server address in environment variable or props, we’ll see how to do it in an other post with development and production modes.

While we receive our response which is a string, we extract only the core of the body with response.text(). If the response was a JSON object we’ll write this instead response.json(). Now that we got the HTTP response we can set isLoading to false and the value we get to dataSource and display it in the JSX part thanks to data binding.

Now you can restart your react native project with expo start --web or expo start --web-only to see the result.

Access-control-allow-origin error

This is not what we expected, but we can easily fix it, don’t worry. The problem here is that your server receive a request from the same IP address as it. It prevents from unusual request. To avoid this kind of error, all we have to do is add a line in our main.ts in your NestJS server :

import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableCors();
  await app.listen(3000);
}
bootstrap();

Security : add app.enableCors(); only in development mode

And now let’s restart the server and reload the page:

It’s working !

WE DIT IT ! I’m so proud of you !

It’s alive

Here the full App.tsx in case you need. Next time we’ll see how to display a list of recipes in our React Native application, from the creation of the mongodb schema, the creation of mongoose class and routes in our server to the displaying of our recipes in the front.

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

interface IMyComponentProps {}

interface IMyComponentState {
  isLoading: boolean;
  dataSource?: string;
}

export default class App extends React.Component<
  IMyComponentProps,
  IMyComponentState
> {
  constructor(props: any) {
    super(props);
    this.state = { isLoading: true };
  }

  componentDidMount() {
    return fetch("http://localhost:3000")
      .then((response) => response.text())
      .then((responseString) => {
        this.setState({
          isLoading: false,
          dataSource: responseString,
        });
      })
      .catch((error) => {
        console.error(error);
      });
  }

  render() {
    if (this.state.isLoading) {
      return (
        <View style={{ flex: 1, padding: 20 }}>
          <ActivityIndicator />
        </View>
      );
    }

    return (
      <View style={styles.container}>
        <Text>{this.state.dataSource}</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
});

That’s all folks, thank you for reading this post, I hope it helped you !

See you in the future