The Index Feature: Building the CatsPage Component
This is Part IV of a eight part series on building a CRUD application with React + Redux. You can view the code for this project here. You can view the table of contents here
In this section, we'll define our component, connect it to the store, and teach it how to receive state from the store and utilize aspects of that state as props.
We'll define our component in src/components/cats/CatPage.js
.
import React, {PropTypes} from 'react';
import {connect} from 'react-redux';
import * as catActions from '../../actions/catActions';
class CatPage extends React.Component {
render() {
return()
}
}
CatPage.propTypes = {
};
function mapStateToProps(state, ownProps) {
}
export default connect(mapStateToProps)(CatPage);
Our empty component has a few key features. Let's take a closer look.
The connect
function
The connect
function is provided by Redux. It subscribes our container component to the store, so that it will be alerted when state changes.
Not every component will be connected, or subscribed, to the store. Only container, or "stateful", components will be connected to the store.
Container vs. Presentational Components
A container component is a component that is connected to the store and aware of application state and changes to that state. It takes that state and passes aspects of it to presentational components as props
.
Container components have functions that dispatch actions that enact our earlier flow of action dispatch -> API call -> action dispatch -> store -> reducer -> component re-render. Container components will pass such functions into presentational components as props.
Presentational components are not aware of the store or our application state. They know about their own props
. They respond to user actions by invoking callback functions that their container component passed them.
These differences will become clearer when we build out our create, edit and delete cat features.
For now, check out this excellent table from the Redux docs
Now, back to our CatsPage component.
The mapStateToProps
Function
Our connect
function takes in an argument: mapStateToProps
, which is a function.
The mapStateToProps
function has a very important job: receive application state from the store whenever state has changed and make data from that data available to the component as props
.
Our CatsPage component needs to display all of the cats, so our mapStateToProps
function should grab the cats from state. Let's do it!
function mapStateToProps(state, ownProps) {
// state = {cats: [{id:1, name: "Maru"}, etc.]}
return {
cats: state.cats
};
}
Our function should return a new object, the key/value pairs of which will become available as props
and their values in our component.
Next up, we want to add some Prop Type validation to our component.
Prop Type Validation
We want to ensure that the cats
property of our component is in fact in array. We should also require that our component receive this property, otherwise it will have nothing to render.
CatsPage.propTypes = {
cats: PropTypes.array.isRequired
};
Now, we're ready to render!
The render()
function
We've already discussed container vs. presentational components and we've come to understand that container components are responsible for how things work--getting data from the store and formatting it, for example.
Presentational components, on the other hand, are in charge of how things look.
So, let's let our container component be a container component. We'll have it render a presentational component to format and display our list of cats.
We'll build a CatList component for the CatPage component to render.
src/components/cats/CatPage.js
import CatList from './CatList';
...
class CatsPage extends React.Component {
render() {
return (
<div className="col-md-12">
<h1>Cats</h1>
<div className="col-md-4">
<CatList cats={this.props.cats} />
</div>
</div>
);
}
}
Our container component is passing the cats collection that it took from state, down to our presentational component, CatList.
Let's go ahead and define our presentational component.
The CatList Component: A Functional Component
We'll define our CatList component as a functional component. React allows us to define components as classes, which is what we've seen so far, or as functions.
For small, stateless (i.e. presentation) components, function definitions provide a clean, eloquent interface.
Functional components take in an argument of props
, which are passed in when the component is called. Functional components don't have an explicit render
method, instead they render whatever is return
ed. For more on functional components, check out this Medium post by Cory House.
We'll define our CatList component as a functional component, and have it accept an argument of cats
, passed in as a prop when the component is called from the CatsPage component.
Our component is simple, it validates the cats
Prop Type, and iterates over cats
to form a collection of, and render, list items that wrap each cat's name.
React Elements and the key
Property
Notice that we are assigning a unique key
property to each list item. React elements that are iteratively constructed, or part of an array, should each receive a unique key
property. React will use this information to efficiently re-render the smallest possible portion of the DOM. For more on how React uses element keys to do this, check out this post.
import React, {PropTypes} from 'react';
const CatList = ({cats}) => {
return (
<ul className="list-group">
{cats.map(cat =>
<li className="list-group-item" key={cat.id}>
{cat.name}
</li>
)}
</ul>
);
};
CatList.propTypes = {
cats: PropTypes.array.isRequired
};
export default CatList;
Now, our app should successfully load all the cats from the API, pass them down to the CatsPage container component, which renders them with the help of the CatList presentational component.
We're ready to build out the individual cat show page!