Dev Container for React Native with Expo
Tags: reactnative, containerapps, react, productivity
Published: 2023-09-22 16:49:30

In this post, I'll show how to create a [Development container](https://containers.dev/) (aka **dev container**) to use it to develop a [React Native](https://reactnative.dev/) app with [Expo](https://expo.dev/).

# What should you know?
- Basic knowledge of Docker
- Basic knowledge of React Native
- Basic knowledge of Expo
- Basic knowledge of Bash

# What do you need?
- [Docker](https://www.docker.com/) installed.
- [Visual Studio Code](https://code.visualstudio.com) installed.
- A Linux box with bash (for Windows users, you can use WSL).
- [Expo client](https://expo.dev/client) on your phone.

# Why dev containers?

In short:
- You prepare the dev environment one time and every developer can use it.
- Dev containers are always ready when you need it.
- Dev containers are always consistent for every developer.
- Dev containers are fully integrated with popular IDE like Visual Studio Code, PyCharm and others.
- You can use them remotely

If you want to go into the details, check the following link from @blackgirlbytes [Why are people developing inside containers? ](https://dev.to/github/why-are-people-developing-inside-containers-38o6)


# Dev Container preparation

Let's prepare the working directory:

```
animus@devto:~$ mkdir my-app
animus@devto:~$ cd my-app
animus@devto:~$ git init -b main
```

Start Visual Studio Code:

```
animus@devto:~$ code .
```

Type **CTRL+SHIFT+P** to run the **Command Palette** and when ready type **Dev Containers: Add Dev Container configuration files**


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2u3cihreoo8z1c1rzpu6.png)


Choose the dev container **Node.js & TypeScript** container like shown below.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o9pw001qnitpaprzn4g5.png)


Choose default image and don't select any features.

After saved the dev container configuration,   you should see a folder named **./devcontainer** with a JSON file **devcontainer.json** containing the default definition of the container.

```
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
{
	"name": "Node.js & TypeScript",
	// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
	"image": "mcr.microsoft.com/devcontainers/typescript-node:1-20-bullseye"

	// Features to add to the dev container. More info: https://containers.dev/features.
	// "features": {},

	// Use 'forwardPorts' to make a list of ports inside the container available locally.
	// "forwardPorts": [],

	// Use 'postCreateCommand' to run commands after the container is created.
	// "postCreateCommand": "yarn install",

	// Configure tool-specific properties.
	// "customizations": {},

	// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
	// "remoteUser": "root"
}
```
Edit this file like shown below:

```
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
{
	"name": "Node.js & TypeScript",
	// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
	"image": "mcr.microsoft.com/devcontainers/typescript-node:1-20-bullseye",

	// Features to add to the dev container. More info: https://containers.dev/features.
	// "features": {},

	// Use 'forwardPorts' to make a list of ports inside the container available locally.
	"forwardPorts": [8081],
	"initializeCommand": "bash .devcontainer/initializeCommand.sh",
	// Use 'postCreateCommand' to run commands after the container is created.
	"postCreateCommand": "bash .devcontainer/postCreateCommand.sh",

	// Configure tool-specific properties.
	// "customizations": {},

	// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
	// "remoteUser": "root",

	"runArgs": ["-p=8081:8081", "--env-file", ".devcontainer/.env"]

}

```

In this new version of this file, I've added:
- **initializeCommand** This section permit to execute a command before the build of the container. I've used this command to execute a script to generate a file of <u>environment variables</u> to use them in the container (the script will be described in the next chapter). 
  ```
  bash .devcontainer/initializeCommand.sh
  ```
- **postCreateCommand** This section permit to execute a command after the build of the container.
  I've used this command to execute a script to install [Expo](https://expo.dev/) and other dependencies like [watchman](https://facebook.github.io/watchman/)
  ```
  bash .devcontainer/postCreateCommand.sh
  ```
- **forwardPorts** I've exposed the port number 8081 to permit the Expo client to access to your app.
  ```
  "forwardPorts": [8081]
  ```
- **runArgs** I've defined an argument to run the container:
  - port binding with the host
  - environment file (generated in the initialize command script)
  ```
    "runArgs": ["-p=8081:8081", "--env-file", ".devcontainer/.env"]
  ```
 
## The initialize command script

The initialize command script **initializeCommand.sh** located in **./devcontainer** folder has the goal to gather your IP address (the script is inspired by [this one](https://stackoverflow.com/questions/38182286/get-interface-name-into-linux-shell-script-variable.)). The address is set in an environment variable named **REACT_NATIVE_PACKAGER_HOSTNAME** used by **Expo** to expose the app. 
This variable will'be printed in the file named **.env** located in the same directory of the script.

The content of the script: 
```
#!/bin/bash
echo "Gathering you ip for dev container"

##############################################################################################
# en (Ethernet) - ib (InfiniBand) - sl (Serial line IP (slip)) - wl (Wireless local area 
network (WLAN)) - ww (Wireless wide area network (WWAN))
#############################################################################################
your_interface_name="eno" 
interface_prefix="en" # Choose the interface network.
iname=$(ip -o link show | sed -rn '/^[0-9]+: en/{s/.: ([^:]*):.*/\1/p}')
ip=`ifconfig $iname  | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | sed 's/inet //g'`

echo "REACT_NATIVE_PACKAGER_HOSTNAME=$ip" > .devcontainer/.env

```

The content of the .env file:
```
REACT_NATIVE_PACKAGER_HOSTNAME=192.168.0.72
```

## The post create command script

The post create command script **postCreateCommand.sh** located in **./devcontainer** folder has the goal to set up **Expo** and **watchman**  as described in the [Expo guide](https://docs.expo.dev/get-started/installation/).

The content of the script:

```
echo -e "\nStarting post creat command script..."
echo "Dev machine:"
uname -a
echo -e "\nInstalling expo boiler plate..."
npm install --save-dev -y create-expo-app@2.1.1
echo -e "\nInstalling watchman...\n"
sudo apt update
sudo apt install watchman
watchman version

echo -e "\n*******************************"
echo -e "\nDev container ready!".
echo -e "\n*******************************\n"
```

# Create and run the Dev Container

Open the **Command Palette** (CTRL+SHIFT+P) and select **_Rebuild and Reopen in Container_**


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sniov1n2h7xti5brn436.png)

# Create the app

To create the app, open a terminal inside the dev container and run the following command:

```
animus@mydevcontainer:~$ npx create-expo-app -t blank myapp
```
# Run the app

To run the app, type the following command:

```
animus@mydevcontainer:~$ cd myapp
animus@mydevcontainer:~$ npx expo start
```
You'll see something like this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0twrsvto4usr2nnczms1.png)

Scan the barcode with the Expo Client App (from your smartphone), and you'll see your app "myapp"!

Have a fun!

# Conclusions

Dev containers are great when you want to prepare a dev environment fully replicable for you and for your team. The little effort that you put at the beginning is well paid off later. Dev containers are useful even in the mobile development using **React Native** with **Expo** improving the productivity of the team.

Suggestions and corrections are welcome.