This tutorial is the second of a three-part series on React by Brad Westfall. This series is all about going beyond basic React skills and building bigger things, like entire Single Page Applications (SPAs). This article picks up where the last article, on React Router, left off.
Article Series:
- React Router
- Container Components (You are here!)
- Redux
In the first article, we created routes and views. In this tutorial, we’re going to explore a new concept in which components don’t create views, but rather facilitate the ones that do. There is code to follow along with at GitHub, if you like to dive right into code.
We’ll also be introducing data to our application. If you’re familiar with with any sort of component-design or MVC patterns, you probably know that it’s generally considered poor-practice to mix your views with application behavior. In other words, while views need to receive data to render it, they shouldn’t know where the data came from, how it changes, or how to create it.
Fetching data with Ajax
As an example of a poor practice, let’s expand on our UserList
component from the previous tutorial to handle its own data fetching:
// This is an example of tightly coupled view and data which we do not recommend
var UserList = React.createClass({
getInitialState: function() {
return {
users: []
}
},
componentDidMount: function() {
var _this = this;
$.get('/path/to/user-api').then(function(response) {
_this.setState({users: response})
});
},
render: function() {
return (
<ul className="user-list">
{this.state.users.map(function(user) {
return (
<li key={user.id}>
<Link to="{'/users/' + user.id}">{user.name}</Link>
</li>
);
})}
</ul>
);
}
});
If you need a more detailed/beginner explanation of what this component is doing, see this explanation.
Why is the example less than ideal?. To start, we’ve broken the rule of mixing “behavior” with “how to render the view” — the two things that should stay separate.
To be clear, there’s nothing wrong with using getInitialState
to initialize component state, and there’s nothing wrong with conducting an Ajax request from componentDidMount
(although we should probably abstract the actual call out to other functions). The problem is that we’re doing these things together in the same component where the view is stored. This tight-coupling makes the application more rigid and WET. What if you need to fetch a list of users elsewhere too? The action of fetching users is tied down to this view, so it’s not reusable.
The second problem is that we’re using jQuery for the Ajax call. Sure, jQuery has many nice features, but most of them deal with DOM rendering and React has its own way of doing this. As for jQuery’s non-DOM features like Ajax, chances are you can find lots of alternatives that are more single-feature focused.
One of those alternatives is Axios, a promise-based Ajax tool that’s very similar (in terms of API) to jQuery’s promise-based Ajax features. How similar are they?
// jQuery
$.get('/path/to/user-api').then(function(response) { ... });
// Axios
axios.get('/path/to/user-api').then(function(response) { ... });
For the remaining examples, we’ll continue to use Axios. Other similar tools include got, fetch, and SuperAgent.
Props and State
Before we get into Container and Presentational Components, we need to clear up something about props and state.
Props and state are somewhat related in the sense that they both “model” data for React components. Both of them can be passed down from parent to child components. However, the props and state of a parent component will become just props to their child component.
As an example, let’s say ComponentA passes some of its props and state to its child, ComponentB. The render
method of ComponentA might look like this:
// ComponentA
render: function() {
return <ComponentB foo={this.state.foo} bar={this.props.bar} />
}
Even though foo
is “state” on the parent, it will become a “prop” on the child ComponentB. The attribute for bar
also becomes a prop on the child component because all data passed from parent to child will become props in the child. This example shows how a method from ComponentB can access foo
and bar
as props:
// ComponentB
componentDidMount: function() {
console.log(this.props.foo);
console.log(this.props.bar);
}
In the Fetching Data with Ajax example, data received from Ajax is set as the component’s state. The example doesn’t have child components, but you can imagine if it did, the state would “flow” down from parent to child as props.
To understand state better, see the React Documentation. From here on, this tutorial will refer to the data that changes over time, as “state”.
It’s time to break up
In the Fetching Data with Ajax example, we created a problem. Our UserList
component works but it’s trying to do too many things. To solve the problem, let’s break the UserList
into two components that each serve a different role. The two component types will be conceptually called Container Components and Presentational Components, a.k.a. “smart” and “dumb” components.
In short, Container Components source the data and deal with state. State is then passed to Presentational Components as props and is then rendered into views.
The terms “smart” vs “dumb” components are going away in the community. I’m only making reference to them here in case you read about them in older articles, so you’ll know they’re the same concept as Container vs Presentational.
Presentational Components
You may not know it, but you’ve already seen Presentation Components before in this tutorial series. Just imagine how the UserList
component looked before it was managing its own state:
var UserList = React.createClass({
render: function() {
return (
<ul className="user-list">
{this.props.users.map(function(user) {
return (
<li key={user.id}>
<Link to="{'/users/' + user.id}">{user.name}</Link>
</li>
);
})}
</ul>
);
}
});
It’s not exactly the same as before, but it is a Presentational Component. The big difference between it and the original is that this one iterates over user data to create list-items, and receives the user data via props.
Presentational Components are “dumb” in the sense that they have no idea how the props they received came to be. They have no idea about state.
Presentational Components should never change the prop data itself. In fact, any component that receives props should consider that data immutable and owned by the parent. While the Presentational Component shouldn’t change the meaningfulness of the data in the prop, it can format the data for the view (such as turning a Unix timestamp into a something more human readable).
In React, events are attached directly to the view with attributes like onClick
. However, one might wonder how events work since Presentational Components aren’t supposed to change the props. For that, we have a whole section on events below.
Iterations
When creating DOM nodes in a loop, the key
attribute is required to be something unique (relative to its siblings). Note that this is only for the highest level DOM node — the <li>
in this case.
Also, if the nested return
looks funky to you, consider another approach which does the same thing by splitting the creation of a list item into its own function:
var UserList = React.createClass({
render: function() {
return (
<ul className="user-list">
{this.props.users.map(this.createListItem)}
</ul>
);
},
createListItem: function(user) {
return (
<li key={user.id}>
<Link to="{'/users/' + user.id}">{user.name}</Link>
</li>
);
}
});
Container Components
Container Components are almost always the parents of Presentational Components. In a way, they serve as a intermediary between Presentational Components and the rest of the application. They’re also called “smart” components because they’re aware of the application as a whole.
Since Container and Presentational Components need to have different names, we’ll call this one UserListContainer
to avoid confusion:
var React = require('react');
var axios = require('axios');
var UserList = require('../views/list-user');
var UserListContainer = React.createClass({
getInitialState: function() {
return {
users: []
}
},
componentDidMount: function() {
var _this = this;
axios.get('/path/to/user-api').then(function(response) {
_this.setState({users: response.data})
});
},
render: function() {
return (<UserList users={this.state.users} />);
}
});
module.exports = UserListContainer;
For brevity, these examples have been leaving out require()
and module.exports
statements. But in this case, it’s important to show that Container Components pull in their respective Presentational Components as a direct dependency. For completeness, this example shows all the requires which would be necessary.
Container Components can be created just like any other React component. They also have a render
methods just like any other component, they just don’t create anything to render themselves. Instead, they return the result of the Presentational Component.
A quick note on ES6 Arrow Functions: You may notice the classic var _this = this
trick needed for the example above. ES6 Arrow functions, besides having shorter syntax, have other benefits which alleviate the need for this trick. To allow you to focus on just learning React, this tutorial avoids ES6 syntax in favor of the older ES5 syntax. However, the GitHub guide for this series makes heavy use of ES6 and it has some explanations in its README files.
Events
So far, we’ve shown how state can be passed from Container to Presentational Components, but what about behavior? Events fall into the category of behavior and they oftentimes need to mutate data. Events in React are attached at the view level. For separation of concerns, this can cause a problem in our Presentational Components if we create event functions where the view is.
To elaborate, let’s start by adding an event to our Presentational Component (a <button>
you can click) directly to identify problems:
// Presentational Component
var UserList = React.createClass({
render: function() {
return (
<ul className="user-list">
{this.props.users.map(function(user) {
return (
<li key={user.id}>
<Link to="{'/users/' + user.id}">{user.name}</Link>
<button onClick={this.toggleActive}>Toggle Active</button>
</li>
);
})}
</ul>
);
},
toggleActive: function() {
// We shouldn't be changing state in presentational components :(
}
});
This would technically work, but it’s not a good idea. Chances are, the event is going to need to change data, and data which changes should be stored as state — something the Presentational Component should not be aware of.
In our example, the state change would be the user’s “activeness”, but you can make up any functions you want to tie to onClick
.
A better solution is to pass functionality from the Container Component into the Presentational Component as a prop like this:
// Container Component
var UserListContainer = React.createClass({
...
render: function() {
return (<UserList users={this.state.users} toggleActive={this.toggleActive} />);
},
toggleActive: function() {
// We should change state in container components :)
}
});
// Presentational Component
var UserList = React.createClass({
render: function() {
return (
<ul className="user-list">
{this.props.users.map(function(user) {
return (
<li key={user.id}>
<Link to="{'/users/' + user.id}">{user.name}</Link>
<button onClick={this.props.toggleActive}>Toggle Active</button>
</li>
);
})}
</ul>
);
}
});
The onClick
attribute is required to be where the view is — at the Presentational Component. However, the function it calls has been moved to the parent Container Component. This is better because the Container Component deals with state.
If the parent function happens to change the state, then the state change will cause a re-render on the parent function which in turn will update the child component. This happens automatically in React.
Here is a demo which shows how the event on the Container Component can change state which will automatically update the Presentational Component:
See the Pen React Container Component Demo by Brad Westfall (@bradwestfall) on CodePen.
Take note of how this example deals with immutable data and makes use of the .bind() method
Using Container Components with the Router
The router should no longer use UserList
directly. Instead, it will use UserListContainer
directly, which in turn will use UserList
. Ultimately, UserListContainer
returns the result of UserList
, so the router will still receive what it needs.
Data Flow and the Spread Operator
In React, the concept of props being passed from parent components down to child components is called flow. The examples so far have only shown simple parent-child relationships, but in a real application there could be many nested components. Imagine data flowing from high-level parent components down through many child components via state and props. This is a fundamental concept in React and is important to keep in mind as we move forward into the next tutorial on Redux.
ES6 has a new spread operator which is very useful. React has adopted a similar syntax to JSX. This really helps React with how data flows via props. The GitHub guide for this tutorial uses it too, so be sure to read the guide documentation on this feature.
Stateless Functional Components
As of React 0.14 (released in late 2015), there is a new feature for creating stateless (Presentational) components even easier. The new feature is called Stateless Functional Components
By now you’ve probably noticed that as you separate your Container and Presentational Components, many of your Presentational ones just have a render method. In these cases, React now allows the component to be written as a single function:
// The older, more verbose way
var Component = React.createClass({
render: function() {
return (
<div>{this.props.foo}</div>
);
}
});
// The newer "Stateless Functional Component" way
var Component = function(props) {
return (
<div>{props.foo}</div>
);
};
You can clearly see that the new way is more compact. But remember, this is only an option for components that just need a render
method.
With the new Stateless Functional way, the function accepts an argument for props
. This means it doesn’t need to use this
to access props.
Here is a very good Egghead.io video on Stateless Functional Components.
MVC
As you’ve probably seen so far, React doesn’t resemble traditional MVC. Often times React is referred to as “just the view layer”. The problem I have with this statement is that it’s too easy for React beginners to believe that React should fit into their familiarity with traditional MVC, as if it’s meant to to be used with traditional controllers and models brought in from third-party.
While it is true that React doesn’t have “traditional controllers”, it does provide a means to separate views and behavior in its own special way. I believe that Container Components serve the same fundamental purpose that a controller would serve in traditional MVC.
As for models, I’ve seen people use Backbone models with React and I’m sure they have all kinds of opinions on whether that worked out well for them. But I’m not convinced that traditional models are the way to go in
React. React wants to flow data in a way that just doesn’t lend itself well to how traditional models work. The Flux design pattern, created by Facebook, is a way to embrace React’s innate ability to flow data. In the next tutorial, we’ll cover Redux, a very popular Flux implementation and what I view as an alternative to traditional models.
Summary
Container Components are more of a concept and not an exact solution. The examples in this tutorial just are one way to do them. The concept though, is so well accepted that it’s even Facebook’s policy to use them within their teams — although they might use different terminology.
This tutorial was highly influenced by other articles on this topic. Be sure to look at this tutorial’s official GitHub guides for even more information and a working example of Container Components.
Article Series:
- React Router
- Container Components (You are here!)
- Redux
Great topic and great article — Looking forward to part 3!
In React, is it possible to get around this type of ugly state code?
According to the React docs,
setState()
performs a shallow merge. This means that theuser
object itself should remain the same, even though the reference to it is copied into a newstate
object. So, why not do this?Based on my understanding, and some quick fiddling around in your CodePen, that should work fine, as it’s essentially exactly the same logic with a lot less code.
What are your thoughts?
Thanks Agop, great question. So what you’re actually doing is mutating
this.state
directly sinceuser
is a reference tothis.state
. See these docs https://facebook.github.io/react/docs/component-api.html . Read the warning section where it says “NEVER mutate this.state directly”.I understand it looks simpler with how you did it, but state mutation is a big deal in React which we don’t get into deeply until the next article. I could have used more tools like lodash or Immutable.js to make my code look nicer, and still adhere to mutation best practices. But I was trying not to introduce more tools into the example. This was just the best way I could think of to do it using vanilla JS (albeit,
Object.assign()
is ES6)Brad, using
Object.assign()
isn’t doing anything to prevent modifying the state. When you callObject.assign()
, you end up with a shallow copy ofthis.state
, which still references the same exact list of users (and thus, the same exact user objects).You can confirm this with this code:
In the console, you’ll see:
So, you’re still modifying the same exact user object living in
this.state
, just through a new reference to it. You can further confirm by adding this after the code that togglesactive
:Here’s a quick pen to demonstrate:
http://codepen.io/agop/pen/ONmJRK
Long story short, all of that code boils down to what I previously wrote:
If you want to truly avoid modifying the state itself, including any of its nested objects, you would need to perform a deep copy of
this.state
. And once we start talking about deep copies, we need to start talking about performance. What if we had 100 users? Are we going to create a deep copy of all 100 users, including all of their properties, just to update 1 property of 1 user?So I had some lengthly Slack discussions with some JS friends and here’s the consensus. For one, we should never mutate
this.state
if we’re adhering to best practices, see my comment from before with the React docs. Secondly, you’re right about how I was usingObject.assign()
and the shallowness. What I should have done is:And made a copy of just the user and not the whole state. Now the test of
user === newUser
is false. The problem now is that because the state is an array, it’s now difficult to “slice” in thenewUser
into it’s original spot. There are various techniques but remember I was trying to avoid new tools like lodash or any complexity that’s off-topic for the pen. One way to do it though is in this alternative codepen http://codepen.io/bradwestfall/pen/NNjWLR if one were to stick to no-new-tools and ES6. Also, again, it’s just one way.For beginner’s sake though, I really just wanted a codepen that would demonstrate passing a function via props from parent to child. I didn’t mean to get into this immutability mess :) So I’ve updated the original pen to do it the very simple mutable way — with a disclaimer that it’s not technically best practice. While your way is even smaller code though, I find it to be confusing to beginners, and off-topic since again the purpose of the pen is to teach something else, and to do it right with immutability, we’d have to get into lots of other stuff which aren’t the purpose of the pen.
Further, if worried about performance, tools like Immutable.js and React’s own Immutability Helpers should serve you well http://facebook.github.io/react/docs/update.html
Brad, I was actually just about to write much of the same stuff, after digging further into React best practices, but you beat me to it. So, that’s right, neither of our original codes (which were identical in terms of what actually happened to the underlying state) adhered to best practices.
As you well know, and accurately described in your comments, your updated version doesn’t adhere to best practices either (new user object, but same users list). I would make some small tweaks that would keep the code simple for the article readers, while totally adhering to React immutability best practices:
Pen:
http://codepen.io/agop/pen/grWbqx
We didn’t modify
this.state
or any of its nested properties, but we kept the code nice and concise (no ES6 usage, keeping it simple). This way you don’t have to get deep into the depths of React and Immutability, without showing a misleading example (let’s be honest, who’s going to read the comments and truly understand that the example is “almost” immutable?).This also answers my own question about deep copies – we don’t need deep copies. We can reference existing objects, and we only need to create shallow copies of stuff that actually changes. As you mentioned in your comment, all of this can be simplified with React’s own immutability helpers, as well as stuff like Immutable-js, etc.
Thanks Agop, I think if anyone reads all this they’ll say the article was easy enough, but what is this immutable thing all about? – those who haven’t been exposed to it at least :) I think our conversation documents it enough for this article. The codepen’s comments actually have a link pointing to this conversation too. I think I’ll keep it as-is because of the main point of the pen being about something else. Thanks for all your feedback and the suggested code.
Brad, it’s your code, your article, and between you and I, we’re clearly both on the same page ;)
Still, I don’t see the purpose of leaving this in the article:
That’s literally dead code. It doesn’t do anything at all (
users[index] === user
before the assignment anyways). It’s just confusing for the reader, and you’re the one that argued against confusing the reader (e.g. “Why is he assigning the user into same list at the same index?”, “Why is he passing the same list back into the state?”, etc.).My code, from start to finish, including comments, is 16 lines, ES5-only, and very easy to understand, even without going into the “whys” of immutability in React. Your code, from start to finish, including comments, is 17 lines.
You say that you don’t want to get into immutability with this article, and that’s fine. Yet, your code has a 5-line comment block talking about how it doesn’t follow immutability best practices and links out to a huge discussion that would make absolutely 0 sense to someone new to React. My code doesn’t make any mention of immutability at all, it just follows it.
Long story short, I’m just trying to understand the thought process behind leaving code that doesn’t adhere to best practices in an article that is already in 5th position when you Google something like “react container components”. It’s frustrating to me to think that someone new to React is going to land on this article, see the example, quickly skip the big block of comments (come on, who reads giant blocks of comments in a CodePen?), and then right below the example, read:
Sorry, I’ll end my rant now.
Thanks Agop, I’ll look at this later :) I’m wrapped up writing the Redux article this week along with some work stuff
Great topic and great articles so far!
Looking forward to part 3 :)
Loving this series, Brad. I’d read both of the articles you referenced as inspiration, but the container vs. presentational concept didn’t really click for me until reading this. Thank you!
Thanks Kyle!
A huge, hearty thank you Brad! CSS-Tricks has helped me get better at my craft over the past few years. Your tutorials are easy to follow which I really like. Looking forward to Part 3! :)
Kind regards,
Loraine Ortiz
Thanks Loraine
This blog post was featured in The React Newsletter #23. http://us4.campaign-archive2.com/?u=29c888baee9c05ccb614e1e92&id=4d59132d66
Hey there,
A very good introduction to React components!
I’ve elaborated this article using slightly different technics, would you be so kind to look into my pen: http://codepen.io/bluurn/pen/xVJNWw
Just wanted to know whether I am correct ( or not :) ).
Thank you very much!
In general it looks pretty good. I noticed the links don’t work though. One reason may be because you did quotes around the curlies
<Link to="{'/users/' + user.id}">{user.name}</Link>
. If an attribute is going to take dynamic parts from JS, then you don’t do the quotes around it. Also, I hope you read part three of this series too because you’ll see how state management is different with Redux – for a multiple component app. Thanks bluurn