Reputation: 189
I'm getting a "TypeError: Cannot read property 'map' of undefined". Not sure where I'm going wrong on this. I'm still pretty new when it comes to React so I don't know if I am missing something or not. It's giving me the error when I'm trying to call this.props.meals.map
export class Dashboard extends React.Component {
componentDidMount() {
this.props.dispatch(fetchProtectedData());
this.props.dispatch(retrieveDailyLogs())
.then(results => {
return this.props.dispatch(getDailyLogs(results));
})
}
getId(id) {
console.log('test');
this.props.dispatch(removeDay(id))
this.props.dispatch(retrieveDailyLogs())
.then(results => {
return this.props.dispatch(getDailyLogs(results));
});
}
render() {
const dailyLogs = this.props.meals.map((day, index) =>
<li className="card" key={index}>
<Card handleClick={(id) => this.getId(id)} {...day} />
</li>
)
return (
<section className="dashboard-container">
<h1>Dashboard</h1>
<Link className="log-day-btn" to="/dailylogs">Log Meals</Link>
<ul className="meal-list">
{dailyLogs}
</ul>
</section>
);
}
}
const mapStateToProps = state => ({
meals: state.dailyLogsReducer.dailyLogs
});
export default requiresLogin()(connect(mapStateToProps)(Dashboard));
Here is my reducer just in case this might help
import {ADD_DAY, GET_DAILYLOGS, DELETE_DAY} from '../actions/dailyLogs';
const initialState = {
dailyLogs: [{
date: null,
meal1: null,
meal2: null,
meal3: null,
snack: null,
totalCalories: null,
}]
};
export default function reducer(state = initialState, action) {
if (action.type === ADD_DAY) {
return Object.assign({}, state, {
dailyLogs: [...state.dailyLogs, {
date: action.date,
meal1: action.meal1,
meal2: action.meal2,
meal3: action.meal3,
snack: action.snack,
totalCalories: action.totalCalories
}]
});
}
else if(action.type === GET_DAILYLOGS) {
return Object.assign({}, state, {
dailyLogs: action.dailyLogs.dailyLogs
})
}
else if(action.type === DELETE_DAY) {
return 'STATE';
}
return state;
}
Here is my combineReducer. It is in my store.js
combineReducers({
form: formReducer,
auth: authReducer,
protectedData: protectedDataReducer,
dailyLogsReducer
}),
Upvotes: 4
Views: 5939
Reputation: 106
Since you declarated dailyLogs in your reducer's initialState as an array, your map should not fail, but only would show us nothing if the data was there. If data is obtained by a async operation, you cant ensure that this data will be there at the rendering moment did by React.
So, we have some points here:
Ensure you won't receive any errors because you tried to use non-undefined operation into a undefined value:
const dailyLogs = this.props.meals
console.log("meals =", dailyLogs); // Will help you know what is this value.
dailyLogs ? dailyLogs.map((day, index) =>
<li className="card" key={index}>
<Card handleClick={(id) => this.getId(id)} {...day} />
</li> : // handle the non-mappable value ...
)
In your reducer, as a good practice, try to use the switch case
statement to explore his benefits
switch (action.type) {
case ADD_DAY:
return // ...
case GET_DAILYLOGS:
return // ...
case DELETE_DAY:
return // ...
default:
return state;
}
And in your switch or if/else statements return, you can do as follows to evolve the state keeping his actuals attributes (spreading):
return {
...state,
dailyLogs: [
// ...
]
};
Keep your code cleaner and concise will help you at all.
Hope it helps someway.
Upvotes: 1
Reputation: 146
The render method can run before componentDidMount does. If this.props.meals is not yet defined by the time the render method runs, your component will throw the error you're seeing.
You can check against its presence before mapping through the object with
this.props.meals && this.props.meals.map( // your code here )
Upvotes: 9