5 very useful React tips you might have never heard of

Lior Zisman
5 min readMar 19, 2018

--

Always stay open for new ideas. Photo by Riccardo Annandale on Unsplash

After I noticed the comments to a recent tweet by Eric Elliott about a pretty clever way to use a debugger inside your JSX, I realised this was not common knowledge. Such simple tricks should be more widely known, so I decided to come up with this list of 5 useful React tips that could improve your code development experience.

#1: Using debugger in JSX

I believe everybody who developed in React for a while found himself trying to make sure his component receives the correct props to render with.
Sure, you can always log the props, but this has a few disadvantages:

  • The console is sometimes messy and chaotic, and not easy to follow.
    Possibly need to distinguish between props printed from different components (which might be of the same class).
  • Having a ‘Snapshot’ of the situation can help you follow the rendering process closely and resolve problems beyond the scope of the props.

When I encountered this situation, I naturally tried to use a debugger statement inside the JSX. The problem? JSX expects expressions, not statements. How do you turn a statement into an expression?

My solution was { eval('debugger') } . Since eval does evaluation (duh) of the expression inside it, I could ‘sneak’ a debugger inside JSX successfully.

Another method I learned, following the tweet I mentioned earlier, is to use an Immediately Invoked Function Expression (IIFE) to use the debugger, and it also lets you add the props right after it to view them:
{ (() => { debugger; this.props; })() } . Thanks Eric!

#2: Passing a function to setState

As you probably know, setState is not synchronous — React may delay it for better perceived performance, and in fact it is actually not guaranteed that the state changes will be applied immediately.

This behaviour can yield unexpected results. A classic example would be:

this.setState({ count: this.state.count + 1});
this.setState({ count: this.state.count + 1});
this.setState({ count: this.state.count + 1});

Assuming this.state.count started as 0, what would be the value of it after these 3 lines? The answer might be surprising — 1. The reason is that the state does not actually update between each call of setState, so in reality, they are all just setting the value of count to 1.

How to solve this? I was pretty amazed it took me that long to find out that setState can receive a function as a parameter! The function must be with the signature (prevState, props) => stateChange .
So our example now looks like this:

const incrementStateCount =
(prevState, props) => ({count: prevState.count + 1});
this.setState(incrementStateCount);
this.setState(incrementStateCount);
this.setState(incrementStateCount);

How is this any different? prevState is a reference to the previous state. Meaning, in a way, we create sequential behaviour by using the function argument to increment the value, since both prevState and props are guaranteed to be up to date. So the final value of this.state.count will be 3.

#3: Dependency Injection using Context

Sometimes you might find yourself in a situation with deeply nested components, forcing your interfaces to allow passing of props through middle-tier ones just so they will be available in the deepest components.
This hurts the readability and maintainability of your code.

Those familiar with Dependency Injection probably can solve this problem by implementing a higher-order component. However, the effort this requires and the effect it has on the code is huge, and it might not solve the problem completely.

This can be solved by using React’s Context API.
Some additional things have to be defined — let’s take a look at this example:

class Title extends React.Component {
render() {
return <span>{this.context.title}</span>;
}
}
Title.contextTypes = {
title: string
}
class Header extends React.Component {
render() {
return <Title />;
}
}
class App extends React.Component {
getChildContext() {
return {title: 'Pancakes are the best!'};
}
render() {
return (
<Header />
);
}
}
App.childContextTypes = {
title: string
}

In this example, the App component will render the title , even though it was never explicitly passed down to the deeply-nested Title component.

What did this require? The main, parent component, needs to specify its childContextTypes , and implement the getChildContext method to provide values. Those will be passed down to any child component that specifies the data it needs in his own contextTypes .

Furthermore, if contextTypes is defined in a component, a few lifecycle methods will receive an additional context / nextContext parameter, providing you with more control.

#4: Error Boundaries

A Javascript error in a part of the UI should not break the entire application.
It’s something you should always consider — gracefully handling errors inside your components. It also benefits the development process greatly.

React 16 introduced the concept of ‘Error Boundaries’. They are components that are meant to catch errors in their child component tree, and handle it gracefully. According to the documentation, “Error boundaries catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them”.

To create an error boundary component, simply make a component that implements the componentDidCatch method. It receives the error and information as arguments, and it is the place to display fallback UI, log the error to a service, and make sure the application can continue operating.

A good example of such a component is described in the documentation:

class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
// Display fallback UI
this.setState({ hasError: true });
// You can also log the error to an error reporting service
logErrorToMyService(error, info);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}

And then use it like this:

<ErrorBoundary>
<MyWidget />
</ErrorBoundary>

#5: Return arrays to avoid redundant elements

Every React developer is probably a bit annoyed when he wraps a list of elements in a render with a span or a div, just because he must return one element from it. This is indeed annoying for a few reasons:

  • This causes rendering of pointless elements to the DOM.
  • Invalid HTML might be created by mistake (where you need correct nesting, for example li inside of ul or td inside of tr).

Turns out, you don’t have to suffer from these effects. Simply return an array of items! Don’t forget to provide a key.

// map returns an array of list items.
// no need to wrap the returned list in an extra element!
const ListItems = ({items}) =>
items.map(item => <li key={item}>{item}</li>);
// can render the list inside a ul and have valid HTML
...
<ul>
<ListItems items={items} />
</ul>
...

React also introduced Fragments, which is a different way to achieve the same result — cleaner DOM, more accurate HTML.

--

--