Design Patterns in React
- Transfer
Design patterns that have emerged and evolved in the React ecosystem over the course of its existence improve readability and cleanliness of the code, and facilitate the reuse of components.
The author of this material says he started 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 come up with a few ideas that have now become popular. Here we can note the transition from to ES6 classes and to pure functional components, rejection of mixins and simplification of the API
Let's start with conditional rendering. I have seen the following scenario in many projects. The point is that while working with React and JSX, developers are still inclined to reflect on these technologies in terms of HTML and JavaScript. As a result, it is only natural that conditional logic is separated from the returned code.
Similar constructions, in which, at the beginning of each function
Alternatively, you can try the following template, using the features of the language.
If it
This allows you to mix user interface logic with a 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. Ultimately, this is just plain JavaScript.
Our next design pattern is passing properties down the component tree (passing down props). When an application grows and develops, it eventually turns out that it consists of small components that act as containers for other components. As a result, it is necessary to transfer large volumes of properties through components that are intended for descendants of these components and are not used by parent components. In such a situation, you can resort to the destructuring of properties, to the use of the extension operator.
In this example, you can modify the properties necessary for
Let's talk about the destructuring props pattern. Over time, the application changes, the same thing happens with the components. A component written a couple of years ago can use state, but under current conditions it can be converted to a stateless component. Often you can see the opposite situation.
Using the possibilities of property restructuring, you can use a useful technique that I use in order to, in the long run, facilitate my work on the project. Properties can be degraded for both types of components.
Please note that lines 2-4 and 11-13 (i.e., tags
The design pattern “provider” refers to the capabilities of React, which appeared relatively recently. Above, we analyzed an example in which properties should be sent to the descendants of a certain component. This operation is complicated by the increase in the number of component-recipient properties. What if, say, properties need to be transferred to the fifteen components? In this situation, it will be useful to use the React Context API . This is not to say that this React feature is useful in any situation, but when it is needed, it comes in handy.
Here it must be said that the appearance of the
In the model we are considering, the top-level component is called a provider. It writes some values to the context. A child component called a consumer takes these values from the context.
While the syntax for working with the context looks a little strange, however, in the next versions of React this particular template is expected to be implemented.
The conversation about the High Order Component (HOC) template is worth starting with the idea of code reuse. Along with the abandonment of the old factory function
A higher order component is a function that takes an input component and returns an extended or modified version of that component. What we call “higher-order components” has many names, I prefer to take them as decorators.
If you use Redux, you will recognize a higher-order component in a function
We implement a simple higher-order component that can add properties to existing components.
If you like functional programming, then you will also like working with higher-order components. Here we can recall the Recompose - excellent package, giving the developer the disposal of such higher-order components such as
Take a look at an example of reusing functionality.
Pay attention to what
Here is an example of cross-cutting functionality and implemented in one place and suitable for reuse throughout the application.
However, higher order components also have disadvantages. Each such component leads to the creation of an additional React component in the DOM / vDOM. This, as the application grows, can lead to potential performance problems.
Some additional issues 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.
The “render props” template, or, as it is also called, “function as a descendant”, allows you to achieve the same that is achievable with higher-order components. These templates are interchangeable. Comparing them, I cannot 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 rendering function to another component, which then returns control through a property that is a function. Some people prefer to use dynamic properties to achieve the same effect, some just use them
Perhaps it would be best to illustrate all this with an example.
Here, as a property, is used
The render props template can be used in situations where you need some logic suitable for reuse inside the component, while this component is not planned to be wrapped in a higher-order component. Examples of using the render props template can be found in the React-Motion library . Here is a component creation example
In the end, I want to note that with the same component, you can use several child functions. The render props template gives the developer unlimited possibilities for composition and reuse of functionality.
In this article, we looked at several React templates, some of which have existed for a long time, and some have appeared relatively recently. We believe that getting to know the templates is useful, as they offer the developer reliable ways to solve typical tasks, the use of which saves time and improves the code.
Dear readers! What templates do you use in your projects based on React?
The author of this material says he started 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 come up with a few ideas that have now become popular. Here we can note the transition from to ES6 classes and to pure functional components, rejection of mixins and simplification of the API
React.createClass
. Now, given the fact that the number of React developers is constantly growing, the fact that serious efforts are being put into the development of this project, one can observe the evolution of several interesting design patterns. These templates are dedicated to this material.Conditional rendering
Let's start with conditional rendering. I have seen the following scenario in many projects. The point is that while working with React and JSX, developers are still inclined to reflect on these technologies in terms of HTML and JavaScript. As a result, it is only natural that conditional logic is separated from the returned code.
const condition = true;
const App = () => {
const innerContent = condition ? (
Show me
Description
) : null;
return (
This is always visible
{ innerContent }
);
};
Similar constructions, in which, at the beginning of each function
render()
, there are conditional ternary operators , tend to quickly get out of control. In order to understand whether this or that element will be displayed, you constantly need to look at the function code. Alternatively, you can try the following template, using the features of the language.
const condition = true;
const App = () => (
This is always visible
{
condition && (
Show me
Description
)
}
);
If it
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 you to mix user interface logic with a 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. Ultimately, this is just plain JavaScript.
Passing properties down the component tree
Our next design pattern is passing properties down the component tree (passing down props). When an application grows and develops, it eventually turns out that it consists of small components that act as containers for other components. As a result, it is necessary to transfer large volumes of properties through components that are intended for descendants of these components and are not used by parent components. In such a situation, you can resort to the destructuring of properties, to the use of the extension operator.
const Details = ( { name, language } ) => (
{ name } works with { language }
);
const Layout = ( { title, ...props } ) => (
{ title }
);
const App = () => (
);
In this example, you can modify the properties necessary for
Details
and ensure that there are no references to these properties in several components.Property Restructuring
Let's talk about the destructuring props pattern. Over time, the application changes, the same thing happens with the components. A component written a couple of years ago can use state, but under current conditions it can be converted to a stateless component. Often you can see the opposite situation.
Using the possibilities of property restructuring, you can use a useful technique that I use in order to, in the long run, facilitate my work on the project. Properties can be degraded for both types of components.
const Details = ( { name, language } ) => (
{ name } works with { language }
);
class Details extends React.Component {
render() {
const { name, language } = this.props;
return (
{ name } works with { language }
)
}
}
Please note that lines 2-4 and 11-13 (i.e., tags
) are identical. This template facilitates the transformation of components. In addition, this approach limits the use this
within the component.Provider Template
The design pattern “provider” refers to the capabilities of React, which appeared relatively recently. Above, we analyzed an example in which properties should be sent to the descendants of a certain component. This operation is complicated by the increase in the number of component-recipient properties. What if, say, properties need to be transferred to the fifteen components? In this situation, it will be useful to use the React Context API . This is not to say that this React feature is useful in any situation, but when it is needed, it comes in handy.
Here it must be said that the appearance of the
Context
new API containing the implementation of the design template "provider" was announcedrelatively recently. This template should be familiar to anyone using something like React Redux or Apollo. In order to deal with the new API, using the existing features, you can experiment with this code . In the model we are considering, the top-level component is called a provider. It writes some values to the context. A child component called a consumer takes these values from the context.
While the syntax for working with the context looks a little strange, however, in the next versions of React this particular template is expected to be implemented.
Higher Order Components
The conversation about the High Order Component (HOC) template is worth starting with the idea of code reuse. Along with the abandonment of the old factory function
React.createElement()
, the React team also refused to support mixins . They were, to some extent, the standard approach to composition of components through the usual composition of objects. Higher-order components are now designed to meet the reuse needs of a multitude of components. A higher order component is a function that takes an input component and returns an extended or modified version of that component. What we call “higher-order components” has many names, I prefer to take them as decorators.
If you use Redux, you will recognize a higher-order component in a function
connect
that, by accepting the component, adds some properties to it. We implement a simple higher-order component that can add properties to existing components.
const withProps = ( newProps ) => ( WrappedComponent ) => {
const ModifiedComponent = ( ownProps ) => ( // модифицированная версия компонента
// исходные свойства + новые свойства
);
return ModifiedComponent;
};
const Details = ( { name, title, language } ) => (
{ title }
{ name } works with { language }
);
const newProps = { name: "Alex" }; // это добавлено компонентом высшего порядка
const ModifiedDetails = withProps( newProps )( Details ); // компонент высшего порядка каррирован для улучшения читабельности
const App = () => (
);
If you like functional programming, then you will also like working with higher-order components. Here we can recall the Recompose - excellent package, giving the developer the disposal of such higher-order components such as
withProps
, withContext
, lifecycle
and so on. Take a look at an example of reusing functionality.
function withAuthentication(WrappedComponent) {
const ModifiedComponent = (props) => {
if (!props.isAuthenticated) {
return ;
}
return ();
};
const mapStateToProps = (state) => ({
isAuthenticated: state.session.isAuthenticated
});
return connect(mapStateToProps)(ModifiedComponent);
}
Pay attention to what
withAuthentication
can be used in situations when you need to display data on the route that is not intended for prying eyes. This data will be available only to users who are logged in. Here is an example of cross-cutting functionality and implemented in one place and suitable for reuse throughout the application.
However, higher order components also have disadvantages. Each such component leads to the creation of an additional React component in the DOM / vDOM. This, as the application grows, can lead to potential performance problems.
Some additional issues 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.
Render props template
The “render props” template, or, as it is also called, “function as a descendant”, allows you to achieve the same that is achievable with higher-order components. These templates are interchangeable. Comparing them, I cannot 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 rendering function to another component, which then returns control through a property that is a function. Some people prefer to use dynamic properties to achieve the same effect, some just use them
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 = () => (
{ ( position ) => (
Hello World
You are at { position }
) }
);
Here, as a property, is used
children
.
We send a function to the component that takes position
as a parameter. The render props template can be used in situations where you need some logic suitable for reuse inside the component, while this component is not planned to be wrapped in a higher-order component. Examples of using the render props template can be found in the React-Motion library . Here is a component creation example
Fetch
illustrating the use of asynchronous streams with the render props template.In the end, I want to note that with the same component, you can use several child functions. The render props template gives the developer unlimited possibilities for composition and reuse of functionality.
Summary
In this article, we looked at several React templates, some of which have existed for a long time, and some have appeared relatively recently. We believe that getting to know the templates is useful, as they offer the developer reliable ways to solve typical tasks, the use of which saves time and improves the code.
Dear readers! What templates do you use in your projects based on React?