Create Action Creator
Let's create our action creator in src/actions/index.js . It should be a function, that accepts some data.
export function selectBook(book) {
console.log('A book has been selected: ', book.title)
}
Then, let's bind action creator with container
...
import {selectBook} from "../actions/index"
class BookList extends Component{
...
Now, let's declare spetial function, that will map data from action creators to props, using a redux fucntion bindActionCreators. Notice, that this function will retrun the data, that will end up in the props of current container.
...
import {bindActionCreators} from 'redux'
...
function mapDispatchToProps(dispatch) {
return bindActionCreators({selectBook: selectBook}, dispatch)
}
...
After that, let's bind this function by react-redux, by passing it to connect function. This will make our props available in this container.
...
function mapDispatchToProps(dispatch) {
return bindActionCreators({selectBook: selectBook}, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(BookList)
Here is the code of the entire class:
import React, {Component} from 'react';
import {connect} from 'react-redux'
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) {
return bindActionCreators({selectBook: selectBook}, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(BookList)
To check if it's working let's add onClick to the elements of the list, where we will select the current item of the element:
...
class BookList extends Component {
renderList() {
return this.props.books.map((book) => {
return (
<li
key={book.title}
onClick={() => this.props.selectBook(book)}
className="list-group-item">
{book.title}
</li>
);
});
}
...
Now we should create an item reducer:
// src/reducers/reducer_active_book.js
export default function (state, action) {
}
State argument is not application state, only the state this reducer is responsible for
To define, what we gonna return, we should add switch which will find out, what type of action we've got here:
export default function (state, action) {
switch (action.type) {
case 'BOOK_SELECTED':
return action.payload;
}
return state
}
If there would be no suitable type in action, the state would be returned. But in preseding code, it is undefined. At least, it should be null:
export default function (state = null, action) {
switch (action.type) {
case 'BOOK_SELECTED':
return action.payload;
}
return state
}
Create a detail component (container)
// src/containers/book-detail.js
import React, {Component} from 'react'
import {connect} from 'react-redux'
class BookDetail extends Component {
render () {
return(
<div>Book Detail</div>
);
}
}
function mapStateToProps(state) {
return {
book: state.activeBook
};
}
export default connect(mapStateToProps)(BookDetail);
Import it into app.js
//src/components/app.js
import React, {Component} from 'react';
import BookList from '../containers/book_list'
import BookDetail from '../containers/book-detail'
export default class App extends Component {
render() {
return (
<div>
<BookList/>
<BookDetail/>
</div>
);
}
}
So, now we should parse data in the item container
// src/containers/book-detail.js
...
class BookDetail extends Component {
render () {
return(
<div>
<h3>Details for:</h3>
<div>{this.props.book.title}</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
book: state.activeBook
};
}
export default connect(mapStateToProps)(BookDetail);
But we have state == null, when we first start the app. So, the screen will be empty, due to the error, and we need to put something into the container view in this case:
// src/containers/book-detail.js
...
class BookDetail extends Component {
render () {
if (!this.props.book) {
return <div>Select a book to get started.</div>;
}
...
And don't forget to combine all our reducers:
// src/reducers/index.js
import {combineReducers} from 'redux';
import BooksReducer from './reducer_books'
import ActiveBook from './reducer_active_book'
const rootReducer = combineReducers({
books: BooksReducer,
activeBook: ActiveBook
});
export default rootReducer;