In my React Redux application, when the main page loads, I want to fetch the data from an API and display it for the user to see. The data is being fetched from the action and the state is being updated. However, I am not seeing the state as a prop of the ponent. Not sure what's not hooked up correctly.
Home ponent :
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as actions from '../actions/actions';
import '../styles/homeStyles.css';
class Home extends Component {
constructor(props) {
super(props);
}
ponentDidMount() {
this.props.actions.fetchMovies();
}
render() {
const { movies, isFetching, errorMessage } = this.props;
if (isFetching && !movies.length) {
return <p>Loading...</p>;
}
//will display movies when I have access to state
return (
<div>
<h1>React Starter App</h1>
<h2>This is the home page</h2>
</div>
);
}
}
Home.proptypes = {
actions: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
movies: PropTypes.array.isRequired,
isFetching: PropTypes.bool.isRequired,
errorMessage: PropTypes.string
};
function mapStateToProps(state) {
return {
movies: state.movies,
isFetching: state.isFetching,
errorMessage: state.errorMessage
};
}
function mapDispatchToProps(dispatch) {
return { actions: bindActionCreators(actions, dispatch) };
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
store :
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';
import { syncHistoryWithStore } from 'react-router-redux';
import { browserHistory } from 'react-router';
import rootReducer from '../reducers/index';
const initialState = {
movies :{
movies: [],
isFetching: false,
errorMessage: null,
firstName: null,
lastName: null,
email: null
}
};
const store = createStore(rootReducer, initialState, applyMiddleware(thunkMiddleware, createLogger()));
export const history = syncHistoryWithStore(browserHistory, store);
if (module.hot) {
module.hot.accept('../reducers/', () => {
const nextRootReducer = require('../reducers/index').default;
store.replaceReducer(nextRootReducer);
});
}
export default store;
App :
import React, { Component } from 'react';
import Navbar from './Navbar';
class App extends Component {
render() {
return (
<div>
<Navbar />
<div className="container">
{this.props.children}
</div>
</div>
);
}
}
export default App;
Index :
import React from 'react';
import {render} from 'react-dom';
import { Provider } from 'react-redux';
import { Router, Route, IndexRoute } from 'react-router';
import store, { history } from './store/store';
require('./favicon.ico');
import App from './ponents/App';
import Home from './ponents/Home';
import Contact from './ponents/Contact';
import NotFoundPage from './ponents/NotFoundPage';
const router = (
<Provider store={store}>
<Router history={history} >
<Route path="/" ponent={App}>
<IndexRoute ponent={Home} />
<Route path="/contact" ponent={Contact} />
<Route path="*" ponent={NotFoundPage} />
</Route>
</Router>
</Provider>
);
render(router, document.getElementById('app'));
Reducer :
import * as constants from '../constants/constants';
const initialState = {
movies: [],
isFetching: false,
errorMessage: null
};
const moviesReducer = (state = initialState, action) => {
switch (action.type) {
case constants.FETCH_MOVIES :
return {
...state,
isFetching: true
};
case constants.FETCH_MOVIES_SUCCESS :
return {
...state,
movies: [action.data],
isFetching: false
};
case constants.FETCH_MOVIES_ERROR :
return {
...state,
isFetching: false,
errorMessage: action.message
};
default :
return state;
}
};
export default moviesReducer;
In my React Redux application, when the main page loads, I want to fetch the data from an API and display it for the user to see. The data is being fetched from the action and the state is being updated. However, I am not seeing the state as a prop of the ponent. Not sure what's not hooked up correctly.
Home ponent :
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as actions from '../actions/actions';
import '../styles/homeStyles.css';
class Home extends Component {
constructor(props) {
super(props);
}
ponentDidMount() {
this.props.actions.fetchMovies();
}
render() {
const { movies, isFetching, errorMessage } = this.props;
if (isFetching && !movies.length) {
return <p>Loading...</p>;
}
//will display movies when I have access to state
return (
<div>
<h1>React Starter App</h1>
<h2>This is the home page</h2>
</div>
);
}
}
Home.proptypes = {
actions: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
movies: PropTypes.array.isRequired,
isFetching: PropTypes.bool.isRequired,
errorMessage: PropTypes.string
};
function mapStateToProps(state) {
return {
movies: state.movies,
isFetching: state.isFetching,
errorMessage: state.errorMessage
};
}
function mapDispatchToProps(dispatch) {
return { actions: bindActionCreators(actions, dispatch) };
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
store :
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';
import { syncHistoryWithStore } from 'react-router-redux';
import { browserHistory } from 'react-router';
import rootReducer from '../reducers/index';
const initialState = {
movies :{
movies: [],
isFetching: false,
errorMessage: null,
firstName: null,
lastName: null,
email: null
}
};
const store = createStore(rootReducer, initialState, applyMiddleware(thunkMiddleware, createLogger()));
export const history = syncHistoryWithStore(browserHistory, store);
if (module.hot) {
module.hot.accept('../reducers/', () => {
const nextRootReducer = require('../reducers/index').default;
store.replaceReducer(nextRootReducer);
});
}
export default store;
App :
import React, { Component } from 'react';
import Navbar from './Navbar';
class App extends Component {
render() {
return (
<div>
<Navbar />
<div className="container">
{this.props.children}
</div>
</div>
);
}
}
export default App;
Index :
import React from 'react';
import {render} from 'react-dom';
import { Provider } from 'react-redux';
import { Router, Route, IndexRoute } from 'react-router';
import store, { history } from './store/store';
require('./favicon.ico');
import App from './ponents/App';
import Home from './ponents/Home';
import Contact from './ponents/Contact';
import NotFoundPage from './ponents/NotFoundPage';
const router = (
<Provider store={store}>
<Router history={history} >
<Route path="/" ponent={App}>
<IndexRoute ponent={Home} />
<Route path="/contact" ponent={Contact} />
<Route path="*" ponent={NotFoundPage} />
</Route>
</Router>
</Provider>
);
render(router, document.getElementById('app'));
Reducer :
import * as constants from '../constants/constants';
const initialState = {
movies: [],
isFetching: false,
errorMessage: null
};
const moviesReducer = (state = initialState, action) => {
switch (action.type) {
case constants.FETCH_MOVIES :
return {
...state,
isFetching: true
};
case constants.FETCH_MOVIES_SUCCESS :
return {
...state,
movies: [action.data],
isFetching: false
};
case constants.FETCH_MOVIES_ERROR :
return {
...state,
isFetching: false,
errorMessage: action.message
};
default :
return state;
}
};
export default moviesReducer;
Share
Improve this question
edited Jul 7, 2016 at 16:35
erichardson30
asked Jul 7, 2016 at 16:02
erichardson30erichardson30
5,0548 gold badges29 silver badges47 bronze badges
5
- Can you show the reducer where movies get updated after they're fetched? – Radio- Commented Jul 7, 2016 at 16:34
-
Put a
debugger
in yourmapStateToProps
function. Does the iningstate
argument contain the data? – lux Commented Jul 7, 2016 at 17:07 - @lux thanks, state was populated but I was accessing them incorrectly – erichardson30 Commented Jul 7, 2016 at 17:12
- @erichardson30: Could you show how did you solve this problem? It will help me. – Vimalraj Selvam Commented Jul 11, 2016 at 6:27
- @VimalrajSelvam posted my solution. Let me know if you need any more help – erichardson30 Commented Jul 11, 2016 at 13:22
1 Answer
Reset to default 4I was accessing state incorrectly in my mapStateToProps function. When I placed a debugger inside the function I was able to see the structure of state and it did not match what I was trying to access there.
Since my store state looked like this :
const initialState = {
movies :{
movies: [],
isFetching: false,
errorMessage: null,
firstName: null,
lastName: null,
email: null
}
};
I needed to change my mapStateToProps function in my ponent to look like this :
function mapStateToProps(state) {
return {
movies: state.movies.movies,
isFetching: state.movies.isFetching,
errorMessage: state.movies.errorMessage
};
}
This allowed everything to be mapped correctly and to have access to the state inside the ponent.
Since then I refactored my state in the store to look like this :
const initialState = {
movies: {
movies: [],
isFetching: false,
errorMessage: null
},
form: {
firstName: null,
lastName: null,
email: null
}
};
and my mapStateToProps function to look like :
function mapStateToProps(state) {
return {
movies: state.movies
};
}