Design patterns that have emerged and developed in the React ecosystem over its lifetime improve the readability and cleanliness of the code, and facilitate the reuse of components.
The author of this material says that he began working with
React about three years ago. At that time, there were no established practices, studying which and following which could improve the quality of their developments.

It took the React community about two years to develop several ideas that have now become popular. Here we can note the transition from
React.createClass
to
React.createClass
classes and to pure functional components, the rejection of mixins and the
simplification of the API . Now, given that the number of React-developers is constantly growing, the fact that serious forces are being invested in the development of this project, one can observe the evolution of several interesting design patterns. This template is dedicated to this material.
Conditional rendering
Let's start with conditional rendering. I have seen the following scenario in a variety of projects. The point is that while working with React and JSX, developers still tend to reflect on these technologies in terms of HTML and JavaScript. As a result, it is quite natural that the conditional logic is separated from the returned code.
const condition = true; const App = () => { const innerContent = condition ? ( <div> <h2><font color="#3AC1EF">Show me</font></h2> <p>Description</p> </div> ) : null; return ( <div> <h1>This is always visible</h1> { innerContent } </div> ); };
Similar constructions, in which, at the beginning of each function
render()
, there are conditional
ternary operators , tend to quickly go out of control. In order to understand whether an element will be displayed, you need to constantly look at the function code.
')
Alternatively, you can try the following pattern that incorporates the features of the language.
const condition = true; const App = () => ( <div> <h1>This is always visible</h1> { condition && ( <div> <h2><font color="#3AC1EF">Show me</font></h2> <p>Description</p> </div> ) } </div> );
If the
condition
is
false
, then the system does not reach the calculation of the second operand of the
&&
operator. If
true
, then the second operand, that is, the JSX code that we want to render, is returned.
This allows the user interface logic to be mixed with the description of the interface elements using a declarative approach. In this case, JSX should be perceived as if it is an integral part of the code. In the end, we are talking about the usual JavaScript.
Passing properties down the component tree
Our next design pattern is passing properties down through the component tree (passing down props). When an application grows and develops, it turns out that it consists of small components that act as containers for other components. As a result, large amounts of properties intended for the descendants of these components and not used by the parent components have to be passed through the components. In such a situation, you can resort to destructuring properties, to the use of the extension operator.
const Details = ( { name, language } ) => ( <div> <p>{ name } works with { language }</p> </div> ); const Layout = ( { title, ...props } ) => ( <div> <h1>{ title }</h1> <Details { ...props } /> </div> ); const App = () => ( <Layout title="I'm here to stay" language="JavaScript" name="Alex" /> );
In this example, you can change the properties required for
Details
and ensure that there are no references to these properties in several components.
Destructuring properties
Let's talk about the pattern of destructuring properties (destructuring props). Over time, the application changes, the same thing happens with components. A component written a couple of years ago can use a state, but under current conditions it can be converted to a stateless component. You can often see the opposite situation.
Involving the possibility of destructuring properties, you can use a useful technique that I use to, in the long run, facilitate the work on the project. Properties can be destructed for components of both types.
const Details = ( { name, language } ) => ( <div> <p>{ name } works with { language }</p> </div> ); class Details extends React.Component { render() { const { name, language } = this.props; return ( <div> <p>{ name } works with { language }</p> </div> ) } }
Notice that lines 2-4 and 11-13 (that is, the
<div>
tags) are identical. This template makes it easy to transform components. In addition, this approach limits the use of
this
inside the component.
Provider Template
The “provider” design pattern refers to React capabilities that have appeared relatively recently. Above, we analyzed an example in which properties should be sent to descendants of a certain component. This operation is complicated with an increase in the number of components receiving properties. How to be, if, say, properties should be transferred to fifteen components? In such a situation it will be useful to use the API
React Context . This is not to say that this React opportunity is useful in any situation, but then, when necessary, it turns out to be very useful.
Here it must be said that the appearance of a new API in
Context
, containing the implementation of the “provider” design pattern, was
announced relatively recently. This template should be familiar to those who use something like React Redux or Apollo. In order to deal with the new API, using existing capabilities, you can experiment with
this code .
In the model we are considering, the component of the upper level is called the provider. It writes some values ​​into the context. A descendant component, called a consumer, takes these values ​​from the context.
While the syntax for working with the context looks a bit strange, however, in the next versions React is expected to implement this particular template.
Higher order components
Talk about the “higher order component” pattern (
High Order Component , HOC) should start with the idea of ​​code reuse. Together with the rejection of the old factory function
React.createElement()
, the React team also refused to support
mixins . They were, to some extent, the standard approach to the composition of components through the usual composition of objects. Higher order components are now designed to meet the need to re-use the functionality of a variety of components.
A higher order component is a function that accepts an input component and returns an extended or modified version of this component. The fact that we here call “higher order components” has many names, I prefer to perceive them as decorators.
If you use Redux, you will recognize a higher-order component in the
connect
function, which, when it accepts a component, adds certain properties to it.
We implement a simple higher order component that can add properties to existing components.
const withProps = ( newProps ) => ( WrappedComponent ) => { const ModifiedComponent = ( ownProps ) => ( // <WrappedComponent { ...ownProps } { ...newProps } /> // + ); return ModifiedComponent; }; const Details = ( { name, title, language } ) => ( <div> <h1>{ title }</h1> <p>{ name } works with { language }</p> </div> ); const newProps = { name: "Alex" }; // const ModifiedDetails = withProps( newProps )( Details ); // const App = () => ( <ModifiedDetails title="I'm here to stay" language="JavaScript" /> );
If you like functional programming, then you will like working with higher-order components. Here you can recall the
Recompose - an excellent package, which gives the developer such higher-order components as
withProps
,
withContext
,
lifecycle
and so on.
Let's look at an example of reuse of functionality.
function withAuthentication(WrappedComponent) { const ModifiedComponent = (props) => { if (!props.isAuthenticated) { return <Redirect to="/login" />; } return (<WrappedComponent { ...props } />); }; const mapStateToProps = (state) => ({ isAuthenticated: state.session.isAuthenticated }); return connect(mapStateToProps)(ModifiedComponent); }
Please note that
withAuthentication
can be used in situations where it is necessary to output data in the route that is not intended for other people's eyes. This data will be available only to logged in users.
Here is an example of
end-to-end functionality and implemented in one place and suitable for reuse throughout the application.
However, higher order components have disadvantages. Each such component leads to the creation of an additional component React in DOM / vDOM. This, as the application grows, can lead to potential performance problems.
Some additional problems with higher order components are described in
this article . Here, in particular, it is proposed to replace higher-order components with a template, which we will now consider.
Template "render props"
The “render props” template, or, as it is also called, “function as a descendant”, allows you to achieve the same that is achievable with the help of higher order components. These templates are interchangeable. Comparing them, I can not give absolute preference to one of them. Both templates are used to make the code cleaner and improve its reusability.
In a nutshell, the idea of ​​using the render props template is to transfer control of your render function to another component, which then returns control through the property that is the function. Some prefer to use dynamic properties to achieve the same effect, some just use
this.props.children
.
Perhaps it would be best to illustrate all this with an example.
class ScrollPosition extends React.Component { constructor( ) { super( ); this.state = { position: 0 }; this.updatePosition = this.updatePosition.bind(this); } componentDidMount( ) { window.addEventListener( "scroll", this.updatePosition ); } updatePosition( ) { this.setState( { position: window.pageYOffset } ) } render( ) { return this.props.children( this.state.position ) } } const App = () => ( <div> <ScrollPosition> { ( position ) => ( <div> <h1>Hello World</h1> <p>You are at { position }</p> </div> ) } </ScrollPosition> </div> );
Here, as a property, is used by
children
. In the
<ScrollPosition>
component, we send a function that takes
position
as an argument.
The render props template can be used in situations where you need some kind of reusable logic inside a component, while this component is not planned to be wrapped into a higher-order component. Examples of using the render props template can be found in the
React-Motion library. Here is
an example of creating a
Fetch
component, illustrating the use of asynchronous streams with the render props template.
As a result, I would like to note that with the same component you can use several descendant functions. Template render props gives the developer unlimited possibilities for composition and reuse of functionality.
Results
In this article, we looked at several React templates, some of which have existed for a long time, and some appeared relatively recently. We believe that familiarity with templates is useful, as they offer the developer reliable methods for solving typical problems, the use of which saves time and improves the code.
Dear readers! What templates do you use in your React-based projects?
