D.R D.R - 20 days ago 13
React JSX Question

react js mapStateToProps triggers Uncaught TypeError: Cannot read property 'map' of undefined

I have an index.js which renders all tables in the database.

_renderAllTables() {
const { fetching } = this.props;

let content = false;

if(!fetching) {
content = (
<div className="tables-wrapper">
{::this._renderTables(this.props.allTables)}
</div>
);
}

return (
<section>
<header className="view-header">
<h3>All Tables</h3>
</header>
{content}
</section>
);
}

_renderTables(tables) {

return tables.map((table) => {
return <Table
key={table.id}
dispatch={this.props.dispatch}
{...table} />;
});
}

render() {
return (
<div className="view-container tables index">
{::this._renderAllTables()}
</div>
);
}
}
const mapStateToProps = (state) => (
state.tables);

export default connect(mapStateToProps)(HomeIndexView);


I changed mapStateToProps from above code to below code.

const mapStateToProps = (state) => ({
tables: state.tables,
currentOrder: state.currentOrder,
});


The reason why I changed code is that I want to use one of state of currentOrder. I have a state which shows whether table is busy or not. So in order to use that I added currentOrder in mapStateToProps. However, it triggers Uncaught TypeError: Cannot read property 'map' of undefined..

How can I use states from other components? any suggestions for that??

Thanks in advance..

--reducer.js

const initialState = {
allTables: [],
showForm: false,
fetching: true,
formErrors: null,
};


export default function reducer(state = initialState, action = {}) {
switch(action.type){
case Constants.TABLES_FETCHING:
return {...state, fetching: true};

case Constants.TABLES_RECEIVED:
return {...state, allTables: action.allTables, fetching: false};

case Constants.TABLES_SHOW_FORM:
return {...state, showForm: action.show};

case Constants.TALBES_RESET:
return initialState;

case Constants.ORDERS_CREATE_ERROR:
return { ...state, formErrors: action.errors };


default:
return state;
}
}

Answer

Your problem is that before fetching successfully tables, your component is rendered with state.tables is undefined.

First of all, best practice is to use selectors rather than json path like state.tables, to be in a separate selectors.js file using reselect lib as follow:

import { createSelector } from 'reselect';

const tablesSelector = state => state.tables;

export default {
  tablesSelector,
}

Second, you need to add reducer for, let's assume, FETCH_ALL_TABLES action using combineReducers from redux lib and most important to initialize tables array with [] before the action is dispatched so as to be defined as follow:

import {combineReducers} from 'redux';

function tables(state = [], {type, payload}) {
  switch (type) {
    case 'FETCH_ALL_TABLES':
      return [
        ...state,
        ...payload,
      ]
  }

  return state;
}


export default combineReducers({
  tables,
});

and in your index.js, may be want update it to:

import selector from './selector';

...

function mapStateToProps(state) {
  return {
    tables: selector.tablesSelector(state),
  }
}
Comments