3
votes

I'm updating my first react/redux app to use redux-immutable and can't figure out why the props in mapStateToProps are empty. I see the properties populated in the thunk logger output but they are empty in the component.

components/Packages.jsx:

import React from 'react';
import pureRender from 'pure-render-decorator';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import {loadPackages} from '../../actions/packageActions';
import PackageList from './PackageList';
import ImmutablePropTypes from 'react-immutable-proptypes';
import {Map,fromJS,List} from 'immutable';

export class Packages extends React.Component {
  constructor(props, context) {
    super(props, context);
  }
  componentDidMount() {
    this.props.dispatch(loadPackages());
  }
  render() {
    return (
      <div className="col-lg-12">
        {this.props.results.length ?
        <PackageList results={this.props.results} /> :
        <h3>No Packages Available</h3>}
      </div>
    );
  }
};

Packages.propTypes = {
  results: ImmutablePropTypes.list.isRequired,
};

const mapStateToProps = (state, ownProps) => {
  return {
    packages: state.get('packages'),
    results: !state.getIn(['packages','results']) ? List() : state.getIn(['packages','results'])
  };
}

export const PackagesContainer = connect(mapStateToProps)(Packages);

actions/packageActions.jsx:

import * as types from './actionTypes';
import PackageApi from '../api/packageApi';

export function loadPackages() {
  return function(dispatch) {
    return PackageApi.getAllPackages().then(packages => {
      dispatch(loadPackagesSuccess(packages));
    }).catch(error => {
      throw(error);
    });
  };
}

export function loadPackagesSuccess(packages) {
  return {
    type: types.LOAD_PACKAGES_SUCCESS,
    state: packages.data
  }
}

reducers/packageReducer.js:

import {Map,fromJS,List} from 'immutable';
import * as types from '../actions/actionTypes';
import initialState from './initialState';

export default function packageReducer(state = initialState, action) {
  switch (action.type) {
    case types.LOAD_PACKAGES_SUCCESS:
      return state.set('packages', fromJS(action.state));
    default:
      return state;
  }
}

reducers/initialState.js:

import {Map, List} from 'immutable';
import update from 'immutability-helper';

const initialState = Map({
  packages: Map({
    totalCount: null,
    results: List(),
    dir: null,
    totalPages: null,
    limit: null,
    sort: null,
    page: null
  })
});
    
export default initialState;

reducers/rootReducer.js:

import {combineReducers} from 'redux-immutable';
import packages from './packageReducer';
import login from './loginReducer';
import { reducer as form } from 'redux-form/immutable';

const reducer = combineReducers({
  packages,
  login,
  form
});

export default reducer;

store.js:

import {createStore, applyMiddleware} from 'redux';
import {createLogger} from 'redux-logger';
import reducer from './reducers/rootReducer';
import thunk from 'redux-thunk';
import initialState from './reducers/initialState';

const store = createStore(reducer, initialState, applyMiddleware(thunk, createLogger()));

export default store;

thunk log:

state:{
  results: (10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
  type: "LOAD_PACKAGES_SUCCESS"
}

Edit

Redux DevTools shows the correct data structure but mapStateToProps still returns the empty List.

Redux DevTools:

{
packages
packages{
  "totalCount": 145,
  "results": [
    {

Packages.jsx:

const mapStateToProps = (state, ownProps) => {
  return {
    packages: state.get('packages'),
    results: !state.getIn(['packages','packages','results']) ? List() : state.getIn(['packages','packages','results'])
  };
}

I updated initialState which fixes the issue with state.getIn(['packages','results']); being undefined. However, the results property still results in an empty list.

2
Have you checked to make sure that the LOAD_PACKAGES_SUCCESS action is correctly updating the store? And is the Packages component being imported from the default (not named) export, to make sure it's wrapped in connect?Shane Cavaliere
How can I verify that the LOAD_PACKAGES_SUCCESS action is correctly updating the store? PackagesContainer is being imported in index.jsx as import {PackagesContainer} from './components/Package/Packages';neridaj
You can use the Redux DevTools extension for Chrome to see the state of your Redux store before and after all dispatched actions.Shane Cavaliere
You have to pass combined reducer i.e., “packages” name as well to state.get like packages:state.get(“packages”, “packages”); in your mapStateToProps. Here first param to state.get is your packages name that you defined in combine reducers and second param is which you set in package reducer. You have to pass like this to get the data in your component.Hemadri Dasari

2 Answers

0
votes

The issue I could see is you are not passing combine reducer name i.e., packages while you do state.get in your components.

Pass packages combine reducer name to state.get or state.getIn methods as a first param in your components.

To get packages from state in your mapStateToProps then get state like below

  packages: state.get(“packages”,”packages”); //here first param is your packages combine reducer name

This applicable for state.getIn as well. You have to pass combined reducer name as a first param to state.get orstate.getIn whenever you combine your reducer.

I hope this should resolve your issue.

0
votes

Changed:

return state.set('packages', action.state);

to

return state.set('packages', fromJS(action.state));

in LOAD_PACKAGE_SUCCESS.