|
From Redux in Action by Marc Garreau and Will Faurot Redux was made for React, and this article discusses how to connect them using the |
Save 37% on Redux in Action. Just enter code fccgarreau into the discount code box at checkout at manning.com.
Connecting Redux and React with react-redux
Redux was built with React in mind, but they’re two totally discrete packages. To connect Redux with React, we’ll use the React bindings from the react-redux
package. Redux provides only the means to configure a store. react-redux
bridges the gap between React and Redux by providing the ability to enhance a component, and allows it to read state from the store or dispatch actions. react-redux
gives us two primary tools for connecting our Redux store to React:
-
Provider
– a React component that you’ll render at the top of the React app. Any components rendered as children ofProvider
can access the Redux store. -
connect
– a function used as a bridge between React components and data from the Redux store.
Adding the Provider component
Provider
is a component that takes the store as a prop and wraps the top-level component in our app — in this case, App
. Any child component rendered within Provider has the ability to access the Redux store, no matter how deeply nested.
In index.js
, import the Provider
component and wrap the App component:
Listing 1 src/index.js
import React from 'react'; import ReactDOM from 'react-dom'; import { createStore } from 'redux'; import { Provider } from 'react-redux'; ❶ import tasks from './reducers'; import App from './App'; import './index.css'; const store = createStore(tasks); ReactDOM.render( <Provider store={store}> ❷ <App /> </Provider>, document.getElementById('root') );
❶ Import the Provider
component
❷ Provider
is now our most top-level React component. It works in conjunction with connect
to make the store available to any child component
Think of the Provider component as an enabler. You won’t often interact with it directly, typically only in a file like index.js
, which takes care of initially mounting the app to the DOM. Behind the scenes, Provider
ensures we can use connect
to pass data from the store to one or more React components.
Passing data from Redux to React components
The groundwork has been laid to pass data from the store into a React component. We have a Redux store with a tasks
reducer, and we’ve used the Provider
component from react-redux
to make the store available to our React components. Now it’s nearly time to enhance a React component with connect
.
Figure 1 The connect method bridges the gap between the store and views (components).
Generally, we can break visual interfaces into two major concerns, data and UI. In our case, the data are the JavaScript objects that represent tasks, and the UI are the few React components that take these objects and render them on the page. Without Redux, we’d deal with both of these concerns directly within React components.
As you can see in figure 2, the data we use to render our UI is moved entirely out of React and into Redux. The App
component is now considered an entry point for data from Redux. As the application grows we’ll introduce more data, more UI, and as a result, more entry points. This kind of flexibility is one of Redux’s greatest strengths. Our application state lives in one place, and we can pick and choose how we want that data to flow into the application.
Figure 2 A visualization of how React and Redux work together.
In listing 2, we’re introducing a couple new concepts: connect
and mapStateToProps
.
By adding connect
to the App component, we’ve declared it as an entry point for data from the Redux store. We’ve only connected one component here, but as our application grows we’ll start to discover best practices for when to use connect
with additional components.
Here we’re passing connect
a single argument, the mapStateToProps
function. Note that the name mapStateToProps
is a convention, not a requirement. The name stuck for a reason, because it’s an effective descriptor of the role of this function. State refers to the data in the store, and props are what get passed in to the connected component. Whatever we return from mapStateToProps
is passed to our component as props.
Listing 2 src/App.js – connecting components
import React, { Component } from ‘react’; import { connect } from ‘react-redux’; import TasksPage from './components/TasksPage'; class App extends Component { render() { return ( <div className="main-content"> <TasksPage tasks={this.props.tasks} /> </div> ); } } function mapStateToProps(state) { ❶ return { tasks: state.tasks ❷ } } export default connect(mapStateToProps)(App);
❶ The state
argument is the entire contents of the Redux store, specifically the result of calling getState
on the store instance
❷ The return value of mapStateToProps
is passed into the App component as props, which is why render
is able to reference this.props.tasks
Notice how you didn’t have to update the TasksPage
component? This is by design. Because TasksPage
accepts its data via props, it doesn’t care about the source of those props. They could come from Redux, from React’s local state, or from another data library altogether.
Container and presentational components
Recall that TaskList
is a presentational or UI component. It accepts data as props, and returns some output according to the markup you’ve defined. By using connect
in the App
component, we’ve secretly introduced their counterparts, known as container components.
Presentational components don’t have dependencies on Redux. They don’t know or care that we’re using Redux to manage our application state. By using presentational components, we’ve introduced determinism into our view renders. Given the same data, we’ll always have the same rendered output. Presentational components then, are easily tested and provide our application with sweet, sweet predictability.
Presentational components are great, but something needs to know how to get data out of the Redux store and pass it to our presentational components. This is where container components, like App
, come in. In this simple example, they have a few responsibilities:
-
Getting data from the Redux store via
connect
-
Using
mapStateToProps
to pass only relevant data to the component being connected -
Rendering presentational components
Again, separating things into container and presentational components is a convention, not a hard and fast rule that React or Redux enforces. But, it’s one of the most popular and pervasive patterns for a reason. It allows us to decouple how our app looks from what it does. Defining our UI as presentational components means we have simple, flexible building blocks which are easy to reconfigure and reuse. When we’re working with data from Redux, we can deal with container components without having to worry about markup. The inverse applies for when we’re working with UI.
At this point, we can view the data being rendered in the browser; our app is rendering a simple list of tasks retrieved from the Redux store. The next logical step would be to wire up some behavior – but that’s a topic for another time.
For more, check out Redux in Actionon liveBook. Also, see this slide deck covering Redux DevTools.