I have this code:
import React, { Component } from 'react';
import { connect } from 'react-redux'; //this is a single connect property
import { selectBook } from '../actions/index';
import { bindActionCreators } from 'redux';
class BookList extends Component {
renderList() {
return this.props.books.map(book => {
return (
<li key={book.title} className="list-group-item"> {book.title} </li>
);
});
}
render() {
return (
<ul className="list-group col-sm-4">
{this.renderList()}
</ul>
)
}
}
function mapStateToProps(state) {
return {
books: state.books
}
}
function mapDispatchToProps(dispatch) {
// Whenver selectBook is called, the result should be passed to all of the reducers.
// selectBook value is an actionCreator, a function.
return bindActionCreators({ selectBook: selectBook }, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(BookList)
and
export function selectBook(book) {
console.log("A book has been seelcted:", book.title)
}
The parts that I don't understand are this:
connect(mapStateToProps, mapDispatchToProps)(BookList)
What is going on? Is this part responsible for making the state and the dispatch method available to the component BookList
?
What is this function doing:
function mapDispatchToProps(dispatch) {
// Whenver selectBook is called, the result should be passed to all of the reducers.
// selectBook value is an actionCreator, a function.
return bindActionCreators({ selectBook: selectBook }, dispatch)
}
Are these built in redux functions? What are they returning? What is going on? What is dispatch
?
The value selectBook
on the right side is a function right? So I guess it gets called, and the return value will flow through the reducers via the dispatch method? But then what is bindActionCreators
doing?
How are props made available inside the component BookList
?
You have too many questions and how hard we try to explain it what is going on here probably confuses you more. I strongly advise you to watch those two videos from the creator of Redux:
Getting started with Redux
and
Idiomatic Redux
Those vidoes explain what is Redux, how does its pieces get together. So, before following any other tutorials first grasp Redux itself.
Also, Redux's official documentation is great, if you follow it alongside with the videos would be great for your study.
But, let me answer your questions anyway.
First question
The parts that I don't understand are this:
connect(mapStateToProps, mapDispatchToProps)(BookList)
What is going on? Is this part responsible for making the state and
the dispatch method available to the component BookList?
Answer
connect
here is provided by react-redux
package, not redux
itself. It is a helper higher order function used to open your state and action creators to your components. So, you guessed right. This is how people generally connect their components into the store to get state and dispatch (to use action creators).
Second question
function mapDispatchToProps(dispatch) {
// Whenver selectBook is called, the result should be passed to all of the reducers.
// selectBook value is an actionCreator, a function.
return bindActionCreators({ selectBook: selectBook }, dispatch)
}
Are these built in redux functions? What are they returning? What is
going on? What is dispatch?
The value selectBook on the right side is a function right? So I guess
it gets called, and the return value will flow through the reducers
via the dispatch method? But then what is bindActionCreators doing?
How are props made available inside the component BookList?
Answer
No, they are not built in Redux functions as I explained in the first answer. mapStateToProps
opens your state to your component and feed it as props. So, if you open any state to your component with connect
and mapStateToProps
you get this state as props. In your example you get the books
state from your global state and open this as books
to your component. Then this component gets this as this.props.books
.
mapDispatchToProps
opens your action creator functions to your component as props. There a couple of ways using this, bindActionCreators
is one of them but actually you don't need it here. It is good for getting state if you need it while you are dispatching or passing your action creators to a child component which does not aware of Redux. So, bindActionCreators
is not a good start to understand mapDispatchToProps
.
Basically it is like that:
const mapDispatchToProps = dispatch => ({
someFunction: () => dispatch(someFunction())
})
Here you are opening your someFunction()
action creator to your component as the name someFunction
. You can use a different name or you can do other staff here and then dispatch
your action creator according to this. But in this example you are not doing anything extra, just dispatching the action creator. So, there is a shorthand for this:
const mapDispatchToProps = {
someFunction,
}
Yes, for this situation this does the same thing. Even, there is a shorter one:
connect(mapStateToProps, {someFunction})(Component)
Without using a mapDispatchToProps
you can use your action creators like this, then get as props in your components.
Also (yes there is more :)) event without using any function argument in connect, we can use action creators.
connect()(Component)
connect(mapStateToProps)(Component)
If we skip mapDispatchToProps
like one of the methods above, dispatch
is automatically passed to the component props. Then, we can use it like any other prop to dispatch our action creator:
this.props.dispatch(someFunction())
For your example it goes like this (I know this app, so I'm using real action creator example here).
You can write mapDispatchToProps
like this:
const mapDispatchToProps = dispatch => ( {
selectBook: book => dispatch( selectBook( book ) ),
} );
Here you use selectBook
as this.props.selectBook
in your component and dispatch an action. You see actually your prop fires a function and it dispatches your real action creator here. Remember, action creators returns objects and those objects need to be dispatched in order to go through reducers. So, you are dispatching your action creator (which is returned by selectBook
.
Now, with bindActionCreators
without using its real advantages you can write it as:
const mapDispatchToProps = dispatch => (
bindActionCreators( { selectBook: selectBook }, dispatch )
);
or with some ES6 shorthand for object keys which has the same name:
const mapDispatchToProps = dispatch => (
bindActionCreators( { selectBook }, dispatch )
);
This is slightly shorter than the first one. You don't need to point a function and dispatch it. You gave the action creator to bindActionCreators
and it does the job for you.
Now, the shorter one since you are just dispatching:
const mapDispatchToProps = {
selectBook,
};
Even without mapDispatchToProps
:
connect( mapStateToProps, { selectBook } )( BookList )
So, Provider
, connect
are being provided by react-redux
to make the life easier for us. mapStateToProps
and mapDispatchToProps
are the functions which connect
waits. Names are not important, we can use any name for them but these are the defacto ones everyone uses. The order is important and if we want to skip mapStateToProps
we must use null
in its place like:
connect( null, mapDispatchToProps )( Component )
Without connect
actually we can use store
and what it contains getState
, dispatch
, subscribe
in any component with two ways:
- Don't use
react-redux
and pass the store
as prop all the way down to each component you want to use. Then reach it via this.props.store
.
- Use
react-redux
's Provider
, then use context
for the component to get the store
.
As you can imagine passing the store all the way down to components is a nightmare.
To use store
with context
first you need to specify the contextTypes
of the component:
BookList.contextTypes = {
store: React.PropTypes.object.isRequired
};
After doing that you can grab store from this.context.store
and use getState
to get the state or dispatch
to dispatch your action creators.
Here how our component would be if we didn't use connect
:
import React, { Component } from "react";
import { selectBook } from "../actions/index";
class BookList extends Component {
renderList() {
return this.context.store.getState().books.map( book =>
( <li
key={book.title}
onClick={() => this.context.store.dispatch( selectBook( book ))}
>
{book.title}
</li> ) );
}
render() {
return (
<ul>
{this.renderList()}
</ul>
);
}
}
BookList.contextTypes = {
store: React.PropTypes.object.isRequired,
};
export default BookList;
Here we use: this.context.store.getState().books
instead of this.props.books
and this.context.store.dispatch( selectBook( book ))
instead of this.props.selectBook( book )
. As you can see we can reach state and can dispatch our action creators with this way. But with connect
and its flexibility we open our state and action creators in a nice and clean way.