Skip to content

Latest commit

 

History

History
183 lines (129 loc) · 11.7 KB

File metadata and controls

183 lines (129 loc) · 11.7 KB

🗺️ Preferred Practices

As we have built our app in React Native, our understanding of how to build software has evolved. As our understanding grows, newer code uses newer techniques. Older code is often left un-updated. It can be difficult to orient oneself around what the current preferred practices are.

This document is a map. Not of Eigen at a specific time, but a map of how we got here and where we want to go next. This is a living document, expected to be updated regularly, of links to:

  • Example code.
  • Pull requests with interesting discussions.
  • Conversations on Slack.
  • Blog posts.

Links should point to specific commits, and not a branch (in case the branch or file is deleted, these links should always work). But it's possible that a file is outdated, that our understanding has moved on since it was linked to; in that case, please update this document.

Contents

Current Preferred Practices

The app is written in Objective-C and Swift, with React Native added in 2016. We only ship an iOS app, and do not yet use React Native for an Android app.

Objective-C and Swift (sometimes called "Native" code) are responsible for the following parts of the app:

  • Sign up/in flow ("onboarding").
  • Live Auctions Integration (LAI) view controller and networking.
  • The Auction view controller.
  • The SwitchBoard (see "SwitchBoard" section below) to navigate between view controllers.
  • The top-level tab bar, and each tab's navigation controller.
  • Deep-link and notification handling (via SwitchBoard).
  • Analytics for Native UI.
  • Initializing the React Native runtime.

Everything else is written in React Native.

Use React Native for new feature development

New features should be built in React Native. The React Native runtime currently requires an existing user ID and access token to be loaded, and sign up/in is still handled in Native code.

We used to have many different renderX functions throughout our components, but today we prefer to have a single render() function in a component. See this PR for our rationale and a comparison of approaches.

Leverage TypeScript to prevent runtime bugs

We use TypeScript to maximize runtime code safety. In April 2020, we adopted TypeScript's strict mode. This disables "implicit any" and require strict null checks. The change left a lot of comments like this throughout the codebase:

// @ts-expect-error STRICTNESS_MIGRATION --- 🚨 Unsafe legacy code 🚨 Please delete this and fix any type errors if you have time 🙏

Our goal is to reduce the number of STRICTNESS_MIGRATION migrations checks to zero over time. We use CI tooling to require PRs never to increase the number. You can opt in to helping out by requiring all the files you change to fix all the migration comments by running the following command:

touch .i-am-helping-out-with-the-strictness-migration

Keep File Structure Organized (in progress)

Everything in src/ is React Native. Within this folder things can be a bit of a mess and we are working on improving that.

Files that export a component end in .tsx, files that don't export a component end in .ts by default.

We use PascalCase for Components and Component Folders, but keep everything else within the Component folder(eg. mutations, state, utils) camelCase. Test files follow the same pattern.

For example mutations, routes, state would be camelCase folders, while MyComponent.tsx would be a PascalCase file.

├── MyComponentFolder
│   ├── MyComponent.tsx
│   ├── MyComponent.tests.tsx
│   ├── mutations
│   |  ├── mutationFunction.ts
│   ├── state
│   |  ├── stateFunction.ts
│   ├── utils
│   |  ├── utilFunction.ts
│   |  ├── utilFunction.tests.ts
├── …

Another example is:

If we have a buttons folder which exports many button components, we keep it lowercase.

├── buttons
│   ├── RedButton.tsx
│   ├── GreenButton.tsx
│   ├── YellowButton.tsx
│   ├── buttons.tests.tsx
│   ├── buttons.stories.tsx
├── …

However, if we have a Button folder which exports only one button component, we write that with in PascalCase.

├── Button
│   ├── Button.tsx
│   ├── Button.tests.tsx
│   ├── Button.stories.tsx

Note: Updating capitalisation on folders can cause issues in git and locally so please refrain from renaming existing folders until we come up with a strategy about this. (TODO)

Use Relay for Network Requests

Data should be loaded from Metaphysics, Artsy's GraphQL server. Requests to Metaphysics should be made through Relay.

Prefer Relay containers (Higher Order Components) over Hooks

We have a preference for Relay containers due to relay-hooks hooks not being compatible with Relay containers which represent the majority of our components using Relay.

styled-system / styled-components

Write unit tests for new components

Unit testing on Emission is a bit all over the place. Some top-level notes:

  • We prefer react-test-render over enzyme, and would ultimately like to remove enzyme.
  • We prefer relay-test-utils over our existing MockRelayRenderer and renderRelayTree.
  • We have native unit tests too. See getting_started.md
  • We don't like snapshot tests; they produce too much churn for too little value. It's okay to test that a component doesn't throw when rendered, but use extractText (or similar) to test the actual component tree.

Here are some great examples of what tests and test coverage should look like.

Use the Native Switchboard for Navigation (for now...)

Our React Native code ("Emission") is used by our Native code ("Eigen"). They used to be two repositories but were combined in February 2020. Traces of the separation remain. The structure we originally took is described in this blog post. Interop between JavaScript and Native can be tricky.

Most interactions are made through a "SwitchBoard" to open links. Other interactions are handled by the APIModules, for example when Eigen needs to invoke some kind of callback.

Analytics

Follow the tracking docs and examples

See our docs on implementing analytics here

Miscellaneous