Understand why patterns in React is such an important topic, and learn why they are used and what problems they came to solve.
In this article, we're going to learn more about advanced patterns in React: what exactly are these patterns, why we started to use them in the first place, and the problems that these patterns came to solve. We'll first learn briefly about Mixins, then about High Order Components, and then Render Props. Also we're going to learn how we can reuse our state logic throughout our components with these patterns, and have better components composition in our applications.
Code reuse was always one of the most important topics in React. The way we build our components to reuse them the most we can so we're not writing too much redundant code was always on the top of priorities in almost every application. The DRY (Don't Repeat Yourself) factor is still pretty relevant these days and it's really important when we're talking about scalability in our applications, so it should be considered every time we're building something new. It's such an important topic for applications. With code reuse, we get a better application, we write less code, and also our code gets more readable, which improves the scalability of our application.
As Mae Capozzi wrote here, there are three levels of code reusability in React (not at all, one-application components, and cross-application components). For me personally, it's like every piece of code should be reused everywhere within the application if it's needed. I agree that sometimes we might write code that we'll not be proud of or not use in another part of our application, but this should be just in special cases. When we're writing code, we should have in mind that we should reuse the most state logic that we can, which lets our code be more readable for other developers. Plus, it'll make our applications more concise and scalable.
React is a beautiful and powerful library, and also a total game-changer in the way we develop applications nowadays. It introduced us developers to a concept that we weren't considering well when we were previously building our applications: composition. The "composition" term can be defined as the way we can build complex functions with small and powerful functions. In our case, we can define it as components, so we can write better components by writing smaller components, reusing as much of our code as possible.
Sometimes we need to build our components in a way that we should reuse its logic in other parts of our application. How we can do it? How we can achieve a good level of composition and code reusability? Write better components, without repeating any data logic? For this, we can use some advanced patterns to achieve this level of composition in React, such as High Order Component or Render Props.
Composition became such an important topic to developers that the community started to look at and develop some solutions for the problem of code and logic repeated throughout our applications. Some patterns deal with code reuse, writing the most reusable code possible, not repeating our state logic, and then writing better components.
Mixins seemed like a good option for code reuse at the beginning of React, back in 2015. If you don't know about mixins, you can read about them in a blog post here, but React has changed so much throughout the intervening years that this pattern is almost unusable today and definitely not a good pattern to use in our applications. The post says "its goal was to give you a way to reuse code between components", but it didn't last too long.
To use the mixins patterns, we needed to use the createClass
method provided to create React class components. A mixin looks like this. We have a function that we want to share:
const fetchJokeMixin = {
getJoke: async () => {
await fetch("https://api.chucknorris.io/jokes/random")
.then(response => response.json())
.then(joke => joke)
.catch(err => err);
}
};
And our React class component would look like this:
React.createClass({
mixins: [fetchJokeMixin],
componentDidMount() {
const joke = this.getJoke();
}
render() {
// ...
}
})
Notice here that the createClass
method provided to create React class component was used to create class components before the ES6 specification got released. Mixins had too many problems with name clashes, performance optimizations, snowballing complexity, and more. It got deprecated very fast because we adopted a new way to deal with code reuse and sharing state logic throughout our components - we started to use High Order Components.
High Order Components can be an answer to code reuse and better composition, and, in fact, it helped and is helping a lot of applications.
To understand High Order Components, we need first take a step back. We need to learn about something fundamental to understanding High Order Components and why they're so powerful and so widely used these days for better component composition and code reuse.
If you're familiar with JavaScript (and I really hope that you are), you may have heard about the High Order Functions. Basically, a high order function is a function that can take another function as an argument, or that returns a function as a result.
For example, the .map
method that is built in to JavaScript is a High Order Function. With it, we can map a specific array and return something. For instance, we can map an array of numbers and return each item multiplied by 2, like this:
const arr1 = [1, 2, 3];
const arr2 = arr1.map(item => item * 2);
console.log(arr2);
In JavaScript, we also have other built-in methods that are High Order Functions, such as the .filter
method. With this method, we create a new array with all elements that pass the condition. We have many more methods in JavaScript that are High Order Functions, and I really recommend you learn more about them.
You may also have heard about the Decorator pattern. Basically, with Decorators, we can wrap one of part of our code (a class or function) and add something to it, without affecting the behavior of other objects from the same code. With it, we can create new functionalities and we can also extend the behavior of our class or function without having to create a new function.
const addStringToName = fn => name => {
const awesomeString = `${name} is awesome.`;
fn(awesomeString);
};
const sayName = name => console.log(name);
const awesome = addStringToName(sayName);
awesome("Leonardo");
Decorators are in stage 2, so maybe in the near future we can start to use them and write more declarative code and with better support.
But why am I talking to you about High Order Functions and Decorators? Well, because they are pretty similar to High Order Components in React.
A High Order Component is basically a function that takes a component as an argument and returns a new component with some more functionality. It's much like a High Order Function, but instead of returning a function, we're returning a component. We can benefit from this pattern and use it with our components to build better components and more reusable code.
Let's imagine that we have a component called Jokes
, and inside that, we have a button. Every time we click that button, we make a request to the Chuck Norris API, and we render some new random joke. Our React class component would be like this:
class Jokes extends Component {
state = {
joke: ""
};
onRequest = () => {
fetch("https://api.chucknorris.io/jokes/random")
.then(response => response.json())
.then(joke => {
this.setState({ joke: joke.value });
})
.catch(err => err);
};
render() {
return (
<div>
<h4>{this.state.joke}</h4>
<button onClick={this.onRequest}>Click to see a new joke</button>
</div>
);
}
};
Now, if we need to use this logic in another component, for example, what we could do? We could use a High Order Component! We could wrap our logic, in that case only the request, and make a High Order Component just for it.
A High Order Component usually looks like this:
const withJoke = JokeComponent => props => {
return class JokeWrapper extends Component {
render() {
return <JokeComponent {...this.props} />;
}
}
}
A High Order Component is basically a function, and inside that function, we're going to wrap a component and pass any additional props that we want to that component. In our case, we want to make a HOC to pass the request logic, so every time we wrap a Component with this HOC it'll include it at that specific component.
So, our High Order Component would look like this:
const withJoke = JokeComponent => {
return class extends Component {
state = {
joke: ""
};
onRequest = () => {
fetch("https://api.chucknorris.io/jokes/random")
.then(response => response.json())
.then(joke => {
this.setState({ joke: joke.value });
})
.catch(err => err);
};
render() {
return (
<JokeComponent {...this.props}
joke={this.state.joke}
onRequest={this.onRequest} />
);
}
};
};
We are passing two props to our wrapped component - joke
that's going to be our joke, and onRequest
that's the function that we're going to make a new request and set a new joke. But now, how can I make use of this HOC in my Jokes component?
Well, inside my Jokes
component now all I need to do is pass the onRequest
prop wherever I want. In this case, I'm going to pass our joke
prop inside a text every time we click on a button. And then I need to wrap the Jokes
component with the withJokes
component at the end of the file.
class Jokes extends Component {
render() {
return (
<div>
<h4>{this.props.joke}</h4>
<button onClick={this.props.onRequest}>Click to see a new joke.</button>
</div>
);
}
}
Now we're reusing the maximum of our logic and repeating less code. You can expand and start to use this pattern in other cases as well - it'll definitely help you to repeat less code and reuse more. As you can see, High Order Components are a powerful pattern, and can help us to both reuse the maximum of code we can and share logic between components easily.
But we also have another nice pattern to share logic across components and reuse code. It's called Render Props. Next, let's see how Render Props can be different from High Order Components.
A render prop is a prop that you pass to a component that tells what this component should render. That's it. Instead of passing a component like in a High Order Component, we pass a function that renders a component. It sounds pretty awesome and easy as well, and, in fact, it is! This is what a render prop looks like:
<FetchJoke render={({ joke, onRequest }) => (
<Jokes joke={joke} onRequest={onRequest} />
)} />
As you can see, it's pretty simple and yet powerful. To explain it further to you, this is what's working under the hood:
Inside the FetchJoke
component, we pass a prop called render
, which is going to be a function to render our Jokes
component. That render
function takes two arguments - joke
which is going to be our joke that we fetch from the API, and onRequest
which is our function that we're going to use to make a new request every time we click a button.
You can see, it's fairly simple and easy to understand, so let's write this code to see this working. We're going to create a new component called FetchJoke
. We copy all the code that we used in our withJoke
HOC previously, but this time we're going to have a class component, and inside the render method, we're going to return the following code:
render() {
return (
<div onClick={this.onRequest}>
{this.props.render(this.state)}
</div>
)
}
As you can see, the render
prop is just a function that renders all our state, in that case, our joke
. And the onRequest
function that we use to make a new request every time we click that button, we pass that inside the div, so every time we make a click we'll render some new joke.
So now, inside our Jokes component, we remove the withJoke
component that we made earlier, and also unwrap it. Then, inside our main App component, we import the FetchJoke
and the Jokes
component, and write the following code:
<FetchJoke render={({ joke, onRequest }) => (
<Jokes joke={joke} onRequest={onRequest} />
)} />
Now we're using the Render Props pattern to render the Jokes
component, and passing some props to it. Awesome!
You might be wondering why the prop that renders our Jokes
component is named render
. That's totally up to you, you can name it the way you want, it's just a best practice to name it to render
or something similar because you can easily understand what's going on under the hood.
In this article we learned about High Order Components and Render Props, how you can use them in your applications, and what problems exactly these patterns came to solve.
Both these patterns are pretty amazing to solve the code reuse problem that a lot of people might have, and to have better component composition throughout our applications. But now, we have a new API that can change everything about the way we were thinking about code reuse. I'm talking about React Hooks.
In the next article, I'm going to talk about React Hooks, how they work and how they can replace these patterns in some cases, and be even better for code reuse and component composition.
Hope you enjoyed this article. See you at the next!
Our KendoReact UI components work quite well with React Hooks - you can read all about it in our recent blog post, Discovering React Hooks with KendoReact.
Leonardo is a full-stack developer, working with everything React-related, and loves to write about React and GraphQL to help developers. He also created the 33 JavaScript Concepts.