The main objectives of this guide are to:
- Reduce overhead in collaborating on RN projects
- Prevent bad practices from creeping into RN projects
- Eliminate mentally-draining decisions when developing with RN
For now, these conventions will be self-enforced for the most part. Ideally we can eventually build these into PR automations.
Not (yet) covered in this guide:
- State management conventions
- Preferred frameworks / libraries
- No HTML errors such as:
div
within abutton
onPress
attached to a div instead of a button
- No linter warnings. If there is an edge case that necessitates breaking code style conventions, insert an ignore flag so the warning goes away
- No copy-pasta code with references to some documetation site
- No component or page files longer than 220 lines
- You have filled out the pull request template and checked off all of the boxes
The following are the top level folders in the React Native project, with guidance on where things should go and how they should be named.
Page:
App/Pages/HomePage/index.js
Domain Component:
App/Pages/HomePage/HomeListView/index.js
Shared Domain Component:
App/Shared/StoreFinder/index.js
State-Management Globals:
App/State/index.js
- Note: Domain specific components and pages can ship their own actions, reducers and such. The above index file is the barrel file that imports all modules
Reusable Services
App/Services/getUserLocation.js
OR
App/Services/getUserLocation/index.js
UI Component:
App/Components/PrimaryButton/index.js
Styling
App/styles/Typography.js
Assets
App/Assets/Images/file.png
Everything's a component, but it's helpful to have context on what the top-level view is just by opening the directory. A Page will be a directory in App/Pages
For example:
App/Pages/HomePage/index.js
Let's consider two types of components:
- UI components, i.e. you would expect to find in a UI framework like Bootstrap
- Domain-specific components (non UI, for keeping files small and striving for Single-responsibility principle)
- Containers, a sub-type of domain-specific components
Presumably, our RN projects will lean on existing UI libraries, such as the standard react-native
library. We will often need to extend these components to match the design. For example, we'll have our own Primary Button
. Here's the convention we'll adopt:
All UI components
will fall into the App/components
directory. They will be defined as a folder with the name of the component, and then an index.{ts,js}
file which holds the component and uses a default export i.e. export default PrimaryButton
. Components, HTML Tags, and Folder Names MUST be identical.
For example:
Component Name: Primary Button
Path: App/components/PrimaryButton/index.js
import statement:
import PrimaryButton from '~/components/PrimaryButton';
Usage:
<PrimaryButton></PrimaryButton>
We'll refer to non-UI components as domain components. When we're
- trying to keep code files small
- trying to ensure each component generally does one thing
we use domain components. An example of a domain component is the StoreFinder
component in Getcho. A domain-specific component will fall in the App/Pages/<page>
directory that it is used in. Thus the directory will look like App/Pages/HomePage/StoreFinder/index.js
. Domain-specific components will nest as far down the chain as possible: App/Pages/HomePage/StoreFinder/ListView/index.js
will be the StoreFinder's listview implementation.
If a domain-specific component is reused across multiple pages, or subpages of components, then place it in the App/Shared
directory. Likewise, a shared domain component may have nested domain components, i.e. App/Shared/StoreFinder/ListView/index.js
.
For components that intend to change global state, we use a pseudo-container convention: end the component with Container. For example: App/Pages/HomePage/StoreFinderContainer/index.js
will indicate to the developer that this store-finder component will integrate with state management. Pages are assumed to be containers and do not need to follow this convention. UI components should never integrate with global state, so this really only applies to DomainSpecificComponents.
App/ See the stylesheets section for more.
Please just use App/assets/<asset_type>/
directory
Most inspiration is drawn from this Thoughtbot article. Checkout their example project for the convention in action.
Most importantly, all styles will be imported as json exports from a file in the App/styles/
directory. The core files we will have in this directory are:
- colors.js
- spacing.js
- typography.js
- buttons.js
Note: If we need to come up with another core file, let's discuss it but it might be reasonable
All of these will be exported from App/styles/index.js
like so:
import * as Buttons from './buttons';
import * as Colors from './colors';
import * as Spacing from './spacing';
import * as Typography from './typography';
export {Typography, Spacing, Colors, Buttons};
You can import like this:
import {Typography, Colors, Spacing} from '../styles';
Then define the styles in the component like this. NOTE we are saving the Stylescheet.create() invocation for the component
const styles = StyleSheet.create({
container: {
...Spacing.horizontalFlex,
backgroundColor: Colors.background,
border: '2px solid black',
},
});
Ideally we keep adhoc style definitions (i.e. 2px solid black
) to a minimum.
Final Note: often the best way to reuse styles is by creating a UI component that can be directly imported rather than reapplying the same styles every time
New RN project linting / style set-up checklist:
- Copy the
.vscode
folder in this directory into the root of the project. If you already have one, mergesettings.json
into your existing - Copy this .prettierrc.js file into the root of the project
- Confirm that prettier is the default formatter by looking in the lower right hand corner of your editor (see screenshot below)
- Confirm that saving reformats a file
Copy this checklist into your README.md and confirm you've done each one!!
This way we have a reasonable assurance that we won't be climbing through ugly git diffs on semantically identical files.
No pushes directly into master.
Always create an issue branch that is issues/<issue_number>
OR issues/<username>/<issue_number>
, and create a Pull Request. Pull Requests should never contain more code changes than what is defined in the issue ticket.
New RN project git set-up checklist:
- Protect the master branch
- Copy the pull_request_template.md into the new project's
.github
directory (create if missing)
Before sending a client a build, (ideally before merging into master), build and run at least on OS version on a device and QA manually.