Skip to main content

    vets.gov Front End Best Practices (WIP)

    Last Updated: April 28, 2017

    We're moving our docs!

    Find the latest version of this page on the Platform website.

    Still can't find what you're looking for? Reach out to #vfs-platform-support on Slack.

    Overview

    This is an initial pass at the best practices followed in vets.gov front-end development.

    Objective

    To promote a consistent approach to vets.gov front-end development by outlining an evolving set of best practices.

    Background

    Over the last year, we have launched a number of different single-page React/Redux apps on vets.gov, in addition to building digital forms using a form-builder library that reuses the same code to run multiple React apps for different forms. This document is an attempt to begin collecting best practices for React/Redux development that the team has and continues to lean toward when architecting and developing front-end applications.

    High Order Guidelines

    This is a placeholder for any high-level paradigms we settle on. For now, it's merely a laundry list of debatable topics:

    • PropTypes: we try to define PropTypes to make it easier for newcomers to understand what can be passed to a component.
    • What to put into a selector vs. reducer vs. action
    • How we map backend data to the state?

    React/Redux Guidelines

    Components vs. containers

    A common React/Redux application architecture is to divide your React components into two types: regular components and container components. These are also sometimes referred to as a dumb and smart components. Container components connect to the Redux store using the react-redux library's connect function and map a specific part of the state object to the props of a React component. Regular components are just plain React components; they take in props and they can have internal state (though we generally avoid this; see below).

    In general, we try to use regular components whenever possible and only a few container components. The reason for this is because tying a component to the Redux store couples it to a particular slice of the state of your application, as well as coupling it to a particular way of organizing your state. So refactoring a lot of container components can be difficult. Debugging can also be difficult with a lot of container components, because they interrupt the usual flow of data down through components. Instead of all data being passed down via props from a single component at the top of the component tree, intermediate components might pull in different parts of the Redux state and pass down that data as props to other components, creating a mix of data combined from different connections to the Redux state.

    There are benefits to using container components, though. It can be painful to pass lots of props all the way down to the leaf components in a component tree and container components allow you to "reset" and grab specific data from the store without all that wiring. They can also improve performance, because passing props down from the root of the component tree means that all intermediate components will re-render whenever data changes. Container components can send data down to their children without all the parents of the container component re-rendering.

    On vets.gov, we normally use a single container component per page (or independent widget, like login), and only use other container components if there's a compelling reason for doing so. Our apps have a containers folder and a components folder that we divide components between.

    Using setState in React components

    We also generally avoid setState inside regular components. This isn't because setState is bad, necessarily, but because it can be hard to track down data changes due to setState when you're expecting all changes to go through the single Redux store. It can also be tricky to keep that state in sync with the data from the Redux store passed in as props. So, when we do use setState, it's typically for ephemeral UI state, or state that would be more difficult to follow if it were put in the store and passed down through props.

    Keep in mind that these are general conventions, not iron-clad rules, and we should revist them as we gain more experience using React and Redux.

    Existing Guides and Tools

    We have a set of ESLint rules that extend the AirBnB style guide and also use the Prettier ESLint rules. There is a pre-commit hook that prevents committing code that fails the ESLint rules. Developers should all be using ESLint plugins in their dev environments to catch lint-able things. In lieu of using an ESLint editor plugin, developers can run the lint:js:fix or lint:js:changed:fix NPM scripts to fix all errors that ESLint can auto-fix.

    TODO: list any notable exceptions here.

    Project Structure and Code Location

    The structure of our React app's is described in How to Start a New ReactJS Project

    A number (and soon all) of our forms use the react-json-schemaform (or rjsf) library. To learn more about those, see schemaform walkthrough and this form config cookbook.

    Existing posts/articles on best practices that we may or may not agree with but can pull topics from: React best practices Redux best practices Long list of links about react architecture and best practices Redux isn't slow, you're just doing it wrong - An optimization guide

    Revision History

    DateRevisions MadeAuthorReviewed By
    March 1, 2017Skeleton document based on outline of Node.js Best Practices
    April 28, 2017Pulled in content from discussion in #659
    May 17, 2017Added information about React component types and setState usage