Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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;
like image 885
erichardson30 Avatar asked Jul 07 '16 16:07

erichardson30


People also ask

How do you pass a state as a prop to another component?

Sending state/props to another component using the onClick event: So first we store the state/props into the parent component i.e in which component where we trigger the onClick event. Then to pass the state into another component, we simply pass it as a prop.

Can state be passed as a prop in React?

Passing state as props from parent to child components is a core concept of React. By keeping state in only a few components and passing it to as many children as needed in the form of props, you will be able to write code that is easier to maintain, and you will thank yourself down the road.

How do I pass a prop to a component?

To pass props, add them to the JSX, just like you would with HTML attributes. To read props, use the function Avatar({ person, size }) destructuring syntax. You can specify a default value like size = 100 , which is used for missing and undefined props.

Can we pass props in Redux?

The function returns a plain object of props that will be passed to the wrapped component. This is useful for conditionally mapping part of the Redux store's state or action creators based on props.


1 Answers

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
  };
}
like image 85
erichardson30 Avatar answered Sep 19 '22 13:09

erichardson30