Reputation: 8448
I am working on this React JS Redux app where I have one store with 2 action files, 2 reducer files and 2 action type files. Everything works good when I am rendering only one component of these action files. If I render both as you see below, I get that the second component's object is undefined:
function App() {
return (
<Provider store={store}>
<div className="App">
< Posts />
< Users />
</div>
</Provider>
);
}
Rendering the above, throws the following error:
Cannot read property 'map' of undefined
If I render the above either omitting </ Posts>
or </ Users>
it works fine and it fetches the related data of each component.
Is it not possible to render two components within the Provider?
For more context, this is what I have in my application so for:
Store.js Includes:
import { createStore, compose, applyMiddleware, combineReducers } from
"redux";
import reduxThunk from "redux-thunk";
import postReducer from "../reducers/postReducer";
import userReducer from "../reducers/userReducer";
const rootReducer = combineReducers({
posts: postReducer,
users: userReducer
});
const store = createStore(rootReducer,
compose(applyMiddleware(reduxThunk)));
export default store;
App.js includes:
import './App.css';
import React from "react";
import store from './store/store.js';
import { Provider } from "react-redux";
import Posts from "./components/postComponent.js"
import Users from "./components/userComponent.js"
function App() {
return (
<Provider store={store}>
<div className="App">
< Posts />
< Users />
</div>
</Provider>
);
}
export default App;
actionTypes.js includes:
export const FETCH_POSTS_STARTED = 'FETCH_POSTS_STARTED';
export const FETCH_POSTS_SUCCESS = 'FETCH_POSTS_SUCCESS';
export const FETCH_POSTS_FAILURE = 'FETCH_POSTS_FAILURE';
export const FETCH_USERS_STARTED = 'FETCH_POSTS_STARTED';
export const FETCH_USERS_SUCCESS = 'FETCH_POSTS_SUCCESS';
export const FETCH_USERS_FAILURE = 'FETCH_POSTS_FAILURE';
userAction.js includes:
import {
FETCH_USERS_STARTED,
FETCH_USERS_SUCCESS,
FETCH_USERS_FAILURE
} from '../actions/actionTypes';
import axios from "axios";
export const fetchUsers = () => {
return dispatch => {
dispatch(fetchUserStarted());
axios.get('https://jsonplaceholder.typicode.com/users')
.then(response => {
// dispatch response here
dispatch(fetchUserSuccess(response.data));
console.log('Data', response.data);
})
.catch(error => {
console.log('Error getting user API ' + error);
dispatch(fetchUserFailure(error.message));
})
}
}
const fetchUserStarted = () => {
return{
type: FETCH_USERS_STARTED,
payload: {
isLoading: true
}
}
}
const fetchUserSuccess = users => {
return{
type: FETCH_USERS_SUCCESS,
payload: {
users
}
}
}
const fetchUserFailure = error => {
return{
type: FETCH_USERS_FAILURE,
payload: {
error
}
}
}
postAction.js includes:
import {
FETCH_POSTS_STARTED,
FETCH_POSTS_FAILURE,
FETCH_POSTS_SUCCESS
} from "./actionTypes";
import axios from "axios";
export const fetchPosts = () => {
return dispatch => {
dispatch(fetchPostsStarted());
axios
.get("https://jsonplaceholder.typicode.com/posts")
.then(res => {
dispatch(fetchPostsSuccess(res.data));
})
.catch(err => {
dispatch(fetchPostsFailed(err.message));
});
};
};
const fetchPostsStarted = () => {
return {
type: FETCH_POSTS_STARTED,
payload: {
isLoading: true
}
};
};
const fetchPostsSuccess = posts => {
return {
type: FETCH_POSTS_SUCCESS,
payload: {
posts
}
};
};
const fetchPostsFailed = error => {
return {
type: FETCH_POSTS_FAILURE,
payload: {
error
}
};
};
userReducer.js includes:
import {
FETCH_USERS_STARTED,
FETCH_USERS_SUCCESS,
FETCH_USERS_FAILURE
} from "../actions/actionTypes.js";
const initialState = {
users: [],
loading: false,
error: null
}
export default function (state = initialState, action) {
switch (action.type) {
case FETCH_USERS_STARTED:
return {
...state,
loading: true
};
case FETCH_USERS_SUCCESS:
return {
...state,
loading: false,
error: null,
users: action.payload.users
};
case FETCH_USERS_FAILURE:
return {
...state,
error: action.payload.error
};
default:
return state
}
}
postReducer.js includes:
import {
FETCH_POSTS_STARTED,
FETCH_POSTS_SUCCESS,
FETCH_POSTS_FAILURE
} from "../actions/actionTypes.js";
const initialState = {
posts: [],
loading: false,
error: null
};
export default function (state = initialState, action) {
switch (action.type) {
case FETCH_POSTS_STARTED:
return {
...state,
loading: true
};
case FETCH_POSTS_SUCCESS:
return {
...state,
loading: false,
error: null,
posts: action.payload.posts
};
case FETCH_POSTS_FAILURE:
return {
...state,
loading: false,
error: action.payload.error
};
default:
return state;
}
}
userComponents.js includes:
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchUsers } from "../actions/action.js";
class Users extends Component {
componentDidMount() {
this.props.fetchUsers();
}
render() {
const { users, loading, error } = this.props;
return (
<div>
{loading && <div>LOADING...</div>}
{error && <div>{error}</div>}
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
}
const mapStateToProps = state => {
const { users, loading, error } = state.users;
return {
users,
loading,
error
};
};
export default connect(
mapStateToProps,
{
fetchUsers
}
)(Users);
postComponents.js includes:
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchPosts } from "../actions/action.js";
class Posts extends Component {
componentDidMount() {
this.props.fetchPosts();
}
render() {
const { posts, loading, error } = this.props;
return (
<div>
{loading && <div>LOADING...</div>}
{error && <div>{error}</div>}
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
}
const mapStateToProps = state => {
const { posts, loading, error } = state.posts;
return {
posts,
loading,
error
};
};
export default connect(
mapStateToProps,
{
fetchPosts
}
)(Posts);
Upvotes: 0
Views: 237
Reputation: 42208
The problem is your action name constants. You copy-and-pasted from one to the other and forgot to change the names!
export const FETCH_POSTS_STARTED = 'FETCH_POSTS_STARTED';
export const FETCH_POSTS_SUCCESS = 'FETCH_POSTS_SUCCESS';
export const FETCH_POSTS_FAILURE = 'FETCH_POSTS_FAILURE';
export const FETCH_USERS_STARTED = 'FETCH_POSTS_STARTED';
export const FETCH_USERS_SUCCESS = 'FETCH_POSTS_SUCCESS';
export const FETCH_USERS_FAILURE = 'FETCH_POSTS_FAILURE';
Since FETCH_USERS_SUCCESS
is actually the same as FETCH_POSTS_SUCCESS
, one type's fetch result is improperly updating the other type's state.
case FETCH_USERS_SUCCESS:
return {
...state,
loading: false,
error: null,
users: action.payload.users
};
Specifically, the error comes about because of the above case
. When we set users
to action.payload.users
on a action which is actually of type 'FETCH_POSTS_SUCCESS'
, the value of action.payload.users
is undefined
. So now state.users.users
is undefined
instead of an array
, and you get a fatal error when trying to call .map()
on it.
Upvotes: 2