Skip to content

Latest commit

 

History

History
executable file
·
142 lines (110 loc) · 4.1 KB

File metadata and controls

executable file
·
142 lines (110 loc) · 4.1 KB

Containers

As you saw in the Provider and connect() readings, there can be quite a bit of code involved in connecting a component to the store. Putting all this code into the component with heavy rendering logic tends to cause bloated components and violates the principle of separation of concerns. Therefore, it's a common pattern in Redux code to separate presentational components from their connected counterparts, called containers.

The distinction between presentational components and containers is not technical but rather functional. Presentational components are concerned with how things look and container components are concerned with how things work.

Here's a table outlining the differences:

Presentational Container
Purpose How things look (markup, styles) How things work (data fetching, state updates)
Aware of Redux No Yes
To Read Data Read data from props Subscribe to Redux state
To Change Data Invoke callbacks from props Dispatch Redux actions
Are Written By hand Generated by React-Redux connect()

Choosing Containers

Not every component needs to be connected to the store. Generally, you will only want to create containers for the 'big' components in your app that represent sections of a page, and contain smaller purely functional presentational components. These larger container components are responsible for mapping state and dispatch props that can be passed down to all their presentational children.

Below is a project file tree that demonstrates how containers might be arranged in a folder for lists.

components
  + list
    + list_container.jsx
    + list.jsx
    + list_item.jsx

Notice that containers exist in the same folder as presentational components and that the container file is named list_container.jsx and not just list.jsx.

In general, aim to have fewer containers rather than more. Most of the components we will write will be presentational, but we'll need to generate a few containers to connect presentational components to the Redux store of our apps.

Let's take a close look at an example with presentational and container components. We'll use the example of lists from the file tree above.

Example

Container Component

// components/list/list_container.jsx

import { connect } from 'react-redux';
import { resetItems } from '../../actions/items' // action creator
import List from '../list'; // presentational component to connect

const mapStateToProps = (state) => ({ // map slice of state to props object
	items: state.items
});

const mapDispatchToProps = (dispatch) => ({ // create action dispatcher
	resetItems: () => dispatch(resetItems());
});

const ListContainer = connect(
	mapStateToProps,
	mapDispatchToProps
)(List);

export default ListContainer;

ListContainer serves as an interface between the store and the component it wraps. It's necessary to import List at the top of the file, as well as actions we plan to dispatch.

Presentational Components

List Component

// components/list/list.jsx

import React from 'react';
import Item from 'components/list/item';

const List = ({ items, resetItems }) => {
	const listItems = items.map((item, idx) => (
		<Item key={idx} item={item} />
	);

	return (
		<div className="list">
			<h1 onClick={resetItems}>
				Click to Reset
			</h1>
			<ul className='list-items'>
				{listItems}
			</ul>
		</div>
	);
};

export default List;

List receives props from ListContainer, deconstructed here as items and resetItems.

In List we pass as props individual items to Item, delegating the responsibility of rendering individual list items to that component.

Item Component

// components/list/item.jsx
import React from 'react';

const Item = ({ item }) => (
	<div className="list-item">
		<h3>
			{item.name}
		</h3>
		<span>
			{item.body}
		</span>
	</div>
);

export default Item;

And finally, Item receives as props a single item, rendering that item's name and body.