erichardson30 erichardson30 - 4 months ago 54
Javascript Question

React Redux pass state as props to component

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 component. Not sure what's not hooked up correctly.

Home component :

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);
}

componentDidMount() {
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 './components/App';
import Home from './components/Home';
import Contact from './components/Contact';
import NotFoundPage from './components/NotFoundPage';

const router = (
<Provider store={store}>
<Router history={history} >
<Route path="/" component={App}>
<IndexRoute component={Home} />
<Route path="/contact" component={Contact} />
<Route path="*" component={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;

Answer

I 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 component 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 component.

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
  };
}