Container vs. Presentational Components in React Redux first appeared on Medium.
For the final project (!!!) at Flatiron School, we were asked to build a SPA app using React Redux with a Rails API. In the project planning phase, I thought about what was βsparking joyβ in the time of the COVID pandemic. It seemed like there were quite a few answers, but the one that stood out most was food. Famous chefs were posting cooking videos to Instagram, good samaritans were donating pizzas to medical staff and essential workers, and it seemed like everyone and their brother was baking bread. Thatβs when I decided β I was going to make a recipe box app called BreadBox.
As I started making a flow chart version of my app, I realized I didnβt quite understand the difference between container and presentational components. Realizing you donβt know a concept is unnerving. (Itβs around this time that you say to yourself, βIβm definitely going to fail this project.β But, there is a tiny voice in the very back of your brain that says, βYou know youβll find a way.β Listen to the tiny voice.) In an effort to understand, I broke it way down for myself by reading a few hundred resources.
Container Components:
- Deal with managing data (typically state)
- They often pass data to child components
Presentational Components:
- Deal with how things look
- Are often reusable
Letβs take a look at an example. As I was first building out my app, I had a single file that looked like this:
components/RecipeList.js
const RecipeList = props => {
const bread = require('../bread-default.jpg');
const recipeCards = props.recipes.length > 0 ? props.recipes.map(r => (
<div className="card" key={r.id}>
<Link to={`/recipes/${r.id}`}>
<h4>{r.attributes.label}</h4>
</Link>
<p><img src={r.attributes.image.length > 0 ? r.attributes.image : bread } width="300" height = "300" alt='bread'/></p><br/>
</div>))
: "You don't have any recipes yet!"
return recipeCards
}
const mapStateToProps = state => {
return {
recipes: state.userRecipes
}
}
export default connect(mapStateToProps)(RecipeList)
Woah β there is a lot going on here. Weβre getting the data AND presenting it. It may work, but this file would be better parsed out, wouldnβt it? Letβs see what it looks like when we break it out into container and presentational components.
containers/RecipeList.js
const RecipeList = props => (
<div>
{props.recipes.map(recipe => (
<RecipeListCard
key={recipe.id}
recipe={recipe} />))
}
</div>
)
const mapStateToProps = state => {
return {
recipes: state.userRecipes
}
}
components/RecipeListCard.js
const RecipeListCard = ({ recipe }) => (
<div className="card">
<Link to={`/recipes/${recipe.id}`}>
<h4>{recipe.attributes.label}</h4></Link>
<p><img src={recipe.attributes.image.length > 0 ? recipe.attributes.image : bread } width="300" height = "300" alt='bread'/></p><br/>
</div>
)
In the container component, we map over the current userβs recipes. We are able to do this by mapping state to props. mapStateToProps takes in the Redux store state and allows us to pick and choose what weβd like to use as a prop (or props) in the RecipeList component. In this case, we use our userRecipes, which returns only that userβs recipes.
Okay, so, weβve mapped over our recipes, and now we are returning individual Recipe List Cards, which take in the deconstructed recipe. Since deconstruction is syntactic sugar from ES6 β we donβt have to say props.recipe.attributes.label, we can just say recipe.attributes.label, etc. Iβve also set a default image Iβve imported called βbread,β in case the user doesnβt upload their own.
It works! Although it looks exactly the same to the user, the back-end has a little more room to breathe. Iβve also just unlocked the reusability factor of using containers. We could easily use the RecipeListCards component on another part of this app (or any app) if we wanted to.
Top comments (0)