In this post, I'll show how to create a Development container (aka dev container) to use it to develop a React Native app with Expo.
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 installed.
- Visual Studio Code installed.
- A Linux box with bash (for Windows users, you can use WSL).
- Expo 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?
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
Choose the dev container Node.js & TypeScript container like shown below.
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 environment variables 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 and other dependencies like 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). 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.
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
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:
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.