The best practices adopted by our engineering team for single-page applications developed with React
React is an excellent choice for building single-page applications. Developed by Facebook and now widely adopted, React is our preferred technology choice for building single-page applications due to its excellent performance and clean component-based architecture.
React structures the user interface into a set of small individual components, each having their own isolated state. The structure makes it very easy to develop reusable components and test them.
One of the most significant advantages of developing applications using React is performance. Rather than manipulate the elements of the page directly, React works with an in-memory representation of the page (the “Virtual DOM”) before copying the changes to the actual HTML tree, delivering much faster user experience.
The practices we use for developing React applications are as follows
- To decide what should be a component
- To develop the structure of the application
- To design the application state - how data affects the state
- Performance optimisation
- Automation and testing
1. Decide what should be a React component
When approaching the development of a new React application, we start with reviewing wireframes and proposed layouts to identify logical blocks. While these will include the navigation, header and footer areas, we also look at the information presented in cards, lists, galleries, buttons and the like.
We split these blocks into smaller and smaller pieces (components). For example, the individual cards contained within a list view are likely to include images, icons and buttons.
To help determine when a component should be broken down further, we review whether that component can be reused in another place or used with other data.
Typically we use components to represent individual items (such as cards, sitemap) and critical elements within those cards (such as images, icons, buttons).
2. Design a clear structure for the React application
We take a modular and highly-structured approach to React applications. The practices we use to organise React applications are as follows:
- Each component lives in a separate folder (under a parent /components folder).
- A single component has a single JSX file – aka “Single Responsibility Principle.”
- Use “Higher-Order Components” to separate presentation from complex logic – so that we split components into dumb (rendering only) and smart (logic without presentation, state management, event handlers) parts whenever possible.
- All files are stored alongside the respective component (JSX, styles, images, etc.).
- All application pages live under a /pages folder – along with their relevant layouts and partials.
- Core application modules – such as services, utilities, helpers, actions, reducers, middleware modules also live in dedicated folders.
The approach makes it very easy to understand everything used by each component.
3. Design the data management state
A single page application depends on data which can come from many different sources (API endpoints, user inputs, device hardware, etc.). A vital part of the design is to decide how to store and best manage this data.
We load data into the redux store if:
- it loads data from the server (i.e. HTTPS requests to API endpoints)
- if the data is rendered in several parts of the page (e.g. message count and list)
- if different components update the same data independently (e.g. search filters)
All authentication information is also stored in the redux store, because any component might rely on this, checking roles/authorisation before showing specific data.
The data should be in a local state of the component if only this component uses this data. For example, the following data are a good candidate to be stored in the local state:
- a value of the input field;
- a validation result (valid or invalid value);
- an active tab;
- a state of a collapsible panel.
4. Consider performance optimisations
By default, React will re-render all components, even when data has not changed. To improve performance, and reduce unnecessary calculation we design our applications to track when components need to be updated – via shouldComponentUpdate()
5. DevOps automation
To support our practices, we use extensive automation, helping to deliver a React application quickly and effectively
- All code is developed using ES6
- ESLint is used – our linting rules are available at https://github.com/graph-uk/eslint-config-graph