Skip to content

Instantly share code, notes, and snippets.

@considine
Created October 26, 2021 18:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save considine/5ff940b43c9f11dd4cc6dc425091e1c1 to your computer and use it in GitHub Desktop.
Save considine/5ff940b43c9f11dd4cc6dc425091e1c1 to your computer and use it in GitHub Desktop.
Copy style guide into public gist

K-Optional Software React Native Style Guide

The main objectives of this guide are to:

  1. Reduce overhead in collaborating on RN projects
  2. Prevent bad practices from creeping into RN projects
  3. Eliminate mentally-draining decisions when developing with RN

New Project Checklist

  • Everything listed under git setup
  • Everything listend under style setup

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

Rules for a pull request

  • No HTML errors such as:
    • div within a button
    • 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

Directory Structure

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

Pages

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

Components

Let's consider two types of components:

  1. UI components, i.e. you would expect to find in a UI framework like Bootstrap
  2. Domain-specific components (non UI, for keeping files small and striving for Single-responsibility principle)
  3. Containers, a sub-type of domain-specific components

UI 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>

Domain-specific Components

We'll refer to non-UI components as domain components. When we're

  1. trying to keep code files small
  2. 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.

Containers

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.

Styles

App/ See the stylesheets section for more.

Assets

Please just use App/assets/<asset_type>/ directory

Stylesheets

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};

Using global styles:

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

Linting and formatting

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, merge settings.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.

Git Conventions

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:

Testing Conventions

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment