9 of the best JavaScript frameworks

Best for:

  • 
Sites and applications with complex view logic
  • Quick prototypes with a low barrier to entry

Launched in 2013, React is maintained by Facebook and Instagram, alongside a community of developers. It's component-based and declarative, and you can also use it to power mobile apps via React Native

Here, we'll explain how to keep your code clean by separating your concerns, move contents outside of the root component, and ensure errors don't destabilise your application.

Use container and presentational components

As with any project, it's important to keep a separation of concerns. All React applications start off simple. As they grow, it can be tempting to keep adding logic to the same few components. In theory, this simplifies things by reducing the number of moving parts. When problems arise, however, these large components become prone to errors that are difficult to debug.

React and JSX encourage the creation on multiple small components to keep things as simple as possible. While breaking the interface down into smaller chunks can help with organisation, having a further separation between how a component works and what it looks like provides greater flexibility.

Container and presentational components are special names given to this separation. The container's job is to manage state and deal with interfacing with other parts of the application such as Redux, while the presentational component deals solely with providing the interface.

class TweetContainer extends Component {
  constructor() {
    super();
    this.state = {
      id: 921329593763680256,
      tweet: 'New issue of Web Designer',
      likes: 10,
    };
    this.like = this.like.bind(this);
  }
  like() {
    this.setState(prevState => ({
      likes: prevState.likes + 1
    })) }
  render() {
    return <Tweet {...this.state} likeTweet={this.like} />
  } }

A container component will often be in charge of a small section of the UI, like a tweet. It will hold all the workings of that component – from storing state, like the number of likes, to the methods required for interaction, such as a mechanism for liking that tweet.

If the application makes use of external libraries, include at this point. For example, Redux's connect method would provide the container with a way of dispatching actions to the store without worrying the presentational component.

const Tweet = ({tweet, likes, likeTweet}) => (
<div>
    Tweet: { tweet }
    Likes: { likes }
    <button onClick={likeTweet}>Like</button>
  </div>
);

Containers will never render their 
own UI and will instead render 
another component – the presentational component.

This component will be passed props that detail all the information needed to render the view. If it needs to provide interactivity, the container will then pass down methods for this as well, which can be called like any other method.

Having this separation encourages developers to keep things as simple 
as possible. If a container is starting 
to grow too large, it makes it easy 
to break off into a smaller set 
of components.

If the inner workings of a component, such as its state, needs to change, this technique allows the presentational component to remain unaffected. This also means this component can be used somewhere else in the application without needing to adjust how it functions. As long as it keeps getting served the same data it will continue to work.

Render with portals

React 16 introduced the ability to return lots of different types of data from a component. While previously it had to be either a single component or 'null', the latest version allows strings, numbers, arrays and a new concept called 'portals'.

The return value of a render() method decides what React displays, which is shown at that point in the component hierarchy. Portals allow React to render any of these return types outside of the component they were called from.

These can be other parts of the page completely separate from the main application. They still form part of React and work just the same as any component, but are able to reach outside of the normal confines of 
the root container.

A typical use case of this technique would be to trigger modal windows. To get correct positioning, overlay 
and accessibility requirements out 
of a modal it ideally needs to sit as a direct descendant of the <body>. The problem is, the root of a single page application will likely take up that position. Components managing modals will either need to trigger something in the root component, or render it out of place.

class Modal extends Component {
  render() {
    return ReactDOM.createPortal(
      this.props.children,
      document.getElementById('modal'),
    ); }}

Here the Modal component returns a portal. The create function for it takes two arguments – what needs to be rendered and where it should render it. The second parameter is a regular DOM node reference, rather than anything specific to React.

In this example, it references a <div> at the top of the DOM tree that is a sibling of the main app container. It is possible to target any node, visible or not, as with any JavaScript. To use it, another component can summon Modal just like any other component. It will 
then display its contents in the targeted node. 

Because React events are synthetic, they are capable of bubbling up from the portal contents to the containing component, rather than the DOM node they are rendered in. In the modal example, this means that 
the summoning component can 
also handle its state, such as its visibility or contents.

Establish error boundaries

Unhandled errors can cause havoc in a JavaScript application. Without catching them as they happen, methods can stop executing half way. This can cause unpredictable behaviour if the user continues and is a bad experience all around.

Previous versions of React did not cope with these situations well. If an error occurred in a nested component, it would leave its parents in limbo. The component state object would be stuck in the middle of performing an operation that could end up locking up the interface.

As of version 16, the way React handles errors has changed. Now an error inside any component would unmount the entire application. While that would stop issues arising with an unstable state, it doesn't lend itself well to a good user experience.

To avoid this, we can create a special component called an error boundary to ring-fence parts of the application from the rest. Any errors that happen inside children of the boundary will not cause issues to those outside of it.

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { errored: false }; }  
  componentDidCatch(error, stackTrace) {
    this.setState({ errored: true });   }
  render() {
    if (this.state.errored) {
      return (
        <h1>An error occurred</h1>
      );     }
    return this.props.children;
} }

Error boundaries work a lot like typical catch blocks in JavaScript. When an error occurs somewhere inside the component tree, it will be caught by the componentDidCatch() method, which receives the error thrown along with a stack trace. When that gets called it is an opportunity to replace the tree with a fresh interface – typically an error message. 

Since it only renders its children, this component 
can wrap others 
to catch any errors that happen within it. The components chosen for this will vary by application, but error boundaries can be placed wherever they are needed, including inside other boundaries.

Error boundary components shouldn't be too complicated. If an error occurs inside of a boundary, it will bubble up to the next boundary up. Failing that, it will unmount the whole application as usual.

Next page: AngularJS