With the recent announcements about Hooks and a new data-caching fetch API, it’s an exciting time to be a React developer. Like a well-plotted TV series in its fifth season, the groundwork laid early on is finally opening up to allow the shock revelations of ReactConf 2018. State in functional components! Context without render functions! And reducers without Redux — complete with some manic laughter from Dan Abramov.
However, these changes are all unstable, and due to be launched in the still-alpha 16.7 release. As tempting as it is to do a yarn add react-next and delete half of your codebase, things could change between now and then, and every article, blog post, and tutorial might become outdated overnight.
Which is fine! Because, in the here-and-now, there’s some new functionality in React 16.6 and Create React App 2 which solves a lot of problems without getting as much fanfare.
Perhaps the most exciting change in React 16.6, React.memo, is getting much less attention than it deserves — which is ironic, as it can directly improve the performance of functional components, which are the beneficiaries of the much-lauded Hooks API.
Perhaps this is because, frankly, it’s how many people expect React to work out of the box. I was quite surprised to discover, while debugging performance issues in a complex React app, that the primary culprits were the “simple” functional components I’d used in place of the much-heavier and surely-more-complex class components. The problem? By default, functional components re-render every time their parent component renders, regardless of whether any state or props have changed (as they lack a componentShouldUpdate method). If you have a lot of nested functional components, traversing the tree for every update can quickly become expensive and slow your application down.
For a while, libraries have added functionality to do a shallow comparison on functional component props, but this is another dependency I have to pull in, and it relies on props always being simple variables. If only there was prior art for, let’s say, caching the result of a function call and re-using it until the arguments change…
Yes: React.memo, as the name suggests, continues React’s flirtation with functional programming by implementing a technique called memoisation, which does just that. By wrapping functional components in a call to React.memo, a component is returned which will not re-render unless the props change.
By default, React.memo will only do a shallow comparison against incoming props. This means that if your props contain an object or an array, your component will continue to re-render regardless of what they contain. You can get around this limitation by passing a function through as a second argument to React.memo, which can perform an equality check against incoming props to determine whether to re-render.
As an added caveat: everywhere I’ve said “render” is technically incorrect, as React will perform its own diffing in the Virtual DOM before determining which nodes to actually redraw. As a result, the React.memo call may have no added impact on your application, and the added checks for equality you use may in fact slow down the render. As with most things in React, this is a tool to be used in very specific circumstances.
You can read more about React.memo in the official documentation.
Sass in Create-React-App
A headline feature of CRA2 is support for Sass “out of the box”, which marks a notable change in their philosophy around CRA’s responsibilities. Since its inception, Create-React-App has been highly opinionated, with an “eject” feature as the only concession to users for whom the defaults from on-high don’t apply.
Meanwhile, Sass has been enjoying a consistent uptick in usage and popularity, even if regular CSS is holding its own for now. According to the 2017 State of JS survey, SASS is now edging out CSS by 18k users to 17k; with an additional 6k users swearing off plain CSS, and 2.7k users wanting to adopt Sass. (And, somehow, 185 developers who have never heard of CSS…)
And so: since October, you’ve been able to pull in a single additional dependency, and then write Sass directly without having to eject, or use convoluted watch scripts. Regardless of where you stand on Sass vs CSS (vs PostCSS, vs LESS, vs Stylus etc etc), or your thoughts on where CRA’s responsibilities end, it’s a positive sign that the team are making the application easier to fit into more people’s real-world workflows.
CSS Modules in Create-React-App
While Sass is a controversial choice because many of its benefits can be achieved through component composition — React’s raison d’être — CSS Modules are a more idiomatic way to represent component styling in a React application. For the uninitiated, CSS Modules work by declaring styles, as normal, in a CSS file appended by “module.css”:
This file can then be imported from within a React component, and used with the regular JS object syntax:
Because these styles are scoped to this component, there’s no need to otherwise namespace the class name, or worry about other rules impacting the presentation of this component (with the obvious exception of element-specific rules). CSS Modules achieve this by creating a globally-unique name for the style; for example, the DOM may represent the above div as:
<div class="style_error__21YPd">Danger, Will Robinson!</div>
The best part? Sass and CSS Modules are not mutually exclusive: which means you can have components with locally-scoped styles that inherit shared variables from master Sass files:
As you can see, there are still a lot of new toys to play with before React 16.7 comes down the chimney later this year. By acquainting yourself with the currently-available features, you — and your codebase — will be in a better position to take advantage of the future build patterns when they’re stable.
At 24 Digital, we keep a keen eye on new technologies, from the exciting future of AR and VR, to the more prosaic incremental version updates that set nerd hearts a-flutter. If you like working on a modern stack to address the needs of the future, we’d love to hear from you!