An Expo, NativeBase, React Native Router Flux & Graphql powered React Native boilerplate. Built to get you up and running at the speed of light! Lightspeed Boilerplate in Action
This boilerplate was designed to alleviate the pain of setting up a new React Native project. The project aims to strike a balance between functionality and cleanliness. You should be able to get up and running quickly without having to strip out a ton of features or undesired options.
Lightspeed is setup to leverage Expo, NativeBase, React Native Router Flux & GraphQL (via Apollo). All the important pieces will be described throughout this documentation and how to remove them if desired. If you feel like there are other key packages missing, feel free to contribute and make a case for them.
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes.
To run the application, you will need to have Node.js (version 10 or newer) installed on your computer. You can download the latest version of Node.js here. Once Node is installed, you will also need to install the Expo CLI by running the following command:
npm install -g expo-cli
-
Clone & Install
# Clone the Repo git clone git@github.com:dpwilhelmsen/lightspeed-boilerplate.git # Navigate to the directory cd lightspeed-boilerplate # Install the dependencies yarn install # OR npm i
-
Start the GraphQL Server
This repo comes with a sample GraphQL endpoint. To run the initial application, you will need to run the server locally. If you choose to use your own endpoint or remove GraphQL functionality, you can safely remove the
server.js
file and remove theserver
script from thescripts
section ofpackage.json
# Start the GraphQL server npm run server
-
Start Expo
expo start
Lightspeed Boilerplate aims to set up code structure in a sensible and maintainable way.
.
├── assets # Assets for the app, handled by Expo's Asset Manager
├── native-base-theme # NativeBase theme definitions
├── src # Source files
│ ├── componenents # Reusable React components
│ ├── constants # Any values that need to be used app wide
│ ├── containers # Components designed to fetch and prepare data for a screen
│ ├── lib # Libraries and services
│ ├── navigation # Contains the route/screen definitions
│ └── screens # All the presentation screen components
├── .eslint.json # Definition of eslint rules to use
├── App.js # Main entry point of the app
├── app.json # Expo configuration options
├── package.json
├── README.md
└── server.js # The demo GraphQl server
Lightspeed Boilerplate's architecture is set up to help with reusability and maintainability. Here we will outline the responsibilities of the various pieces.
These are standard React components. Presentational, functional & form components all reside here.
Containers are intended to fetch and prepare data for a page. These containers utilize GraphQL to submit queries and mutations, with the results being passed along to the screens.
As an example, let's look at the HomeContainer
...
const HomeContainer = ({ screen: Screen }) => {
return (
<Query query={HOME_DATA}>
{({
loading, error, data,
}) => {
if (loading) return <LoadingScreen>Fetching</LoadingScreen>;
if (error) return <ErrorScreen>Error</ErrorScreen>;
const { homepage } = data;
return <Screen homepage={homepage} />;
}}
</Query>
);
};
...
This container is using Apollo's Query
component to fetch the data for the home page. Based on the state of the query,
the container decides which screen to render, the LoadingScreen
, ErrorScreen
or HomeScreen
. This gives you a nice,
clean central location to handle loading, errors and data transformation.
It's important to note that not every screen needs a container. The SettingsScreen
is an example of having a screen
with no server interaction.
If you with to fetch your data via other means, you can remove the GraphQL specific code from the containers and replace it with
a package of your choosing. Here's a quick example of using fetch
to aquire the data for the screen.
import React, { Component } from 'react';
class HomeContainer extends Component {
constructor(props) {
super(props);
this.state = {
loading: true,
errors: null,
data: null,
}
}
componentDidMount() {
fetch('https://api.mydomain.com')
.then(response => response.json())
.then(data => this.setState({ data, loading: false }))
.catch((errors) => this.setState({ errors, loading: false }));
}
render() {
const { screen: Screen } = this.props;
const { loading, errors, data } = this.state;
if (loading) return <LoadingScreen>Fetching</LoadingScreen>;
if (errors) return <ErrorScreen errors={errors}>Error</ErrorScreen>;
return (<Screen homepage={data} />);
}
}
export default HomeContainer;
You could pair this with redux or some sort of caching solution to prevent unnecessary refetches.
Screens are what's actually displayed on screen. Screens can either be solo or paired with a container. They assume data has been passed in, where applicable, and render the screen they are responsible for.
Each screen imports the Layout
component. This component is the shared layout between all screens. It contains
the header, content area and footer (if desired). The layout accepts a few different props to allow per screen variations.
Screens & Components are imported & tied together in the AppNavigator
as outlined below.
The route definitions for the application are located within AppNavigator
. It utilizes React Native Router Flux, which itself uses React Navigation.
In the AppNavigator, the router, stack and scenes are defined. In this initial setup, it also creates a drawer
menu, which get be removed if undesired.
The Scene
component is what ties together containers and screens. If your screen needs data, you want to pass in the container as the component
prop
and pass in the screen component as the screen
prop:
<Scene
hideNavBar
key="home"
component={HomeContainer}
screen={HomeScreen}
/>
If you do not need a container for the screen, the screen can just be passed into the component
prop:
<Scene
hideNavBar
key="settings"
component={SettingScreen}
/>
Expo is a set of tools, libraries and services to empower React Native development. Utilizing it can help you get up and running very quickly. The documentation for Expo can be found here.
While Expo is very useful, there may be situation where you wish to remove it. Often, you'll wish to utilize a native library that's unavailable while using Expo. In those cases, you can eject from Expo.
Detailed instructions for ejecting to ExpoKit (Expo platform tools) can be found here. It would be best to follow their instructions but the main steps are:
-
Ensure the proper configurations options are set in
app.json
.{ "expo": { "name": "Your App Name", "icon": "./path/to/your/app-icon.png", "version": "1.0.0", "slug": "your-app-slug", "sdkVersion": "XX.0.0", "ios": { "bundleIdentifier": "com.yourcompany.yourappname" }, "android": { "package": "com.yourcompany.yourappname" } } }
-
Run
expo eject
. This will download the required dependencies and build native projects under the ios and android directories. -
Now that you have a native app, you can proceed to set up ExpoKit following the steps outlined in their Developing with ExpoKit page.
NativeBase is a UI kit that provides cross-platform UI components. It's simple to theme and the library provides a consistency between platforms and devices.
Removing NativeBase is a bit involved as it's used widely throughout the boilerplate. However, most components have React Native equivalents so it should be easy to replace.
- Go through
screens
andcomponents
directories and replace any usage of NativeBase components with their React Native equivalents. NativeBase docs outline what each component replaces - Remove all the NativeBase icon fonts loaded in
Font.loadAsync
inApp.js
- Remove the package using
yarn remove native-base
ornpm uninstall native-base
.
React Native Router Flux is a declaritive react native router. It's based on the React Navigation plugin and as such, you can utilize React Navigation tools, like the withNavigation
HOC if needed.
React Native Router Flux is primarily utilized in the AppNavigator
component. Since there are many variants of navigation, we can only provide a rough outline of how to remove.
- Replace or remove the contents of
AppNavigator
and it's inclusion inApp.js
. Replace with your new navigation system. - Replace any import of
Actions
in the components and screens with the equivalent method of navigating in your new navigation package.
Apollo client is used to query the GraphQL server that provides the data for the application. It is highly configurable and straightforward to use to consume and parse endpoints.
- Remove references to
<Query>
,<Mutation>
, andgraphql-tag
from the containers. - Remove
<ApolloProvider>
fromApp.js
. - Remove
lib\client.js
. - Remove
server.js
and remove theserver
command from the scripts section ofpackage.json
- Uninstall the Apollo package.
Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.
This project is licensed under the MIT License - see the LICENSE file for details