Frontend Best Practices

Overview

  • CSS
    • Pre/Postprocessors
    • Organization
    • Naming Conventions
  • Javascript Conventions
    • Style guides
    • Linters
  • React Tips & Tricks
  • Automated Testing
    • Integration tests vs unit tests
    • Testing React Components
    • Testing Redux

CSS

Pre/Post-processors

  • LESS/SASS
    • write styles in languages that compiles to CSS
  • PostCSS
    • The Future™
    • parses CSS into node tree, applies JS transformations, then outputs transformed styles
  • should we use one? yes
  • does it matter which one we use? not really

Pre/Post-processors cont'd

  • Useful features
    • variables
    • nested selectors (&)
    • functions
    • unit conversions
    • vendor prefixing
  • We already have cssnext/PostCSS installed, just need to add plugins and configure Webpack properly

Organization

  • Currently: one CSS file per React component, all inlined separately onto every page (not ideal) 
    • encourages duplication of styles
    • unclear order of style precedence
    • easy to find styles for components
  • Better Option: Put CSS in separate directory, structured by components/pages/shared, and compile all CSS files into one file before serving
    • allows for style re-use when appropriate
    • clear order of style precedence
    • slightly harder to find styles for components

Naming Conventions

  • motivation: make CSS codebase easy to comprehend and maintain
  • describe how classes relate to each other
  • establish hierarchy without high-specifity selectors
  • BEM, SMACSS
  • can get pretty complicated; start with only the most useful parts
  • Javascript namespaces (e.g. js- prefix)
    • decouple JS/DOM interaction from styling

BEM

/*
 * Block, Element, Modifier
 * everything is a class; nothing is nested
 */

/* a block is the top-level abstraction */
.modal {}

/* an element is a child of a block */
.modal__content {}

/* a modifier changes the behavior of a block/element */
.modal--open {}

/*
 * benefits:
 * - low-specifity, shallow selectors
 * - modularity
 * - easy to understand what classes are for
 */

References

Javascript Conventions

Style Guides

  • the exact code conventions aren't that important
  • what matters is that everyone follows the same conventions
  • more detailed conventions === less things to think about for the 
  • my personal favorite is the AirBnB style guide (they also have a React style guide) (and an .eslintrc!)

Linters

  • the preferred linter in 2016 seems to be ESLint
  • we already have ESLint installed and configured in the frontend
  • configurations are managed in .eslintrc
  • Atom, Sublime Text, Vim, etc. all have ESLint plugins that will highlight linter errors as you write code
  • ideally, PRs should not be merged if they contain code that fails the linter
  • can use eslint --fix to automatically fix linter errors

React Tips & Tricks

Writing Good Components

  • keep components small; better to have more small components than a few large, complicated ones
  • separate data fetching and interaction logic (state) from UI details
    • higher-level components know how to get data and what to do with it
    • lower-level components know what things should look like

Props

  • propTypes are your friend
    • specifies the interface of a component, i.e. what data do you need to use this component?
    • can specify both type and requiredness of props, as well as nested prop structures
  • defaultProps are also your friend

Component Lifecycle

  • don't interact with the DOM in constructor(); use componentDidMount() instead
  • don't request data (via AJAX or websockets) in constructor(); use componentWillMount() instead
  • don't assign to this.state directly; use this.setState()
  • use componentDidUpdate() to trigger side-effects of state changes

References

Automated Testing

Unit vs. Integration

  • unit tests ensure that components and modules behave correctly and conform to expected interface
  • integration tests check end-to-end behavior of the frontend, starting from user interaction and ending with updates to the DOM
  • writing tests is kind of a pain
  • integration tests provide more value for time spent

Writing Integration Tests

  1. Visit the url of the page in question
  2. Mock responses for any backend API requests
  3. Perform user interaction (e.g. click a button) through testing tools
  4. Wait for asynchronous behavior (e.g. backend requests) to finish executing
  5. Assert expectations about the state of the DOM (e.g. the button is now red)

Testing Tools

  • For unit tests, React docs suggest using Jest
  • Other options include EnzymeMocha, Jasmine
  • For integration testing, it'd be nice to use a headless browser testing library like CasperJS or Nightwatch.js
  • Running integration tests would involve spinning up an instance of the app, running headless browser tests, then spinning down the app instance
    • ideally a script could automate this

Testing Tool Considerations

  • ease of writing/debugging tests
    • can tests run in a browser?
    • how fast do tests run?
  • does the testing library place nicely with React/Redux?
  • how to mock backend requests/signals?
  • can tests be run in random order?
  • can tests be run by a continuous integration system?

Frontend Best Practices

By Oren Shoham

Frontend Best Practices

  • 332