I am getting this error:
I have this reducer for async:
const initUserState = {
fetching: false,
fetched: false,
users: [],
error: null
};
const userReducer = (state = initUserState, action) => {
switch(action.type){
case "FETCH_USER":
state = {
...state,
users : action.payload
};
break;
case "FETCH_USER_START":
state = {
...state,
fetching: true
};
break;
case "FETCH_USER_SUCCESS":
state = {
...state,
fetched: true,
users: action.payload
};
break;
case "FETCH_USER_ERROR":
state = {
...state,
fetched: false,
error: action.payload
};
break;
default:
break;
}
return state;
};
export default userReducer;
And my container is:
import React from 'react';
import { Blog } from '../components/Blog';
import { connect } from 'react-redux';
//import { act_fetchAllUser } from '../actions/userActions';
import axios from 'axios';
class App extends React.Component {
componentWillMount(){
this.props.fetchAllUser();
}
render(){
console.log("Prop: " , this.props.user);
return(
<div>
<h1>My Blog Posts</h1>
<Blog/>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
user: state.userReducer
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetchAllUser: () => {
dispatch((dispatch) => {
dispatch({ type: "FETCH_USER_START" })
axios.get('http://infosys.esy.es/test/get_allBlog.php')
.then((response) => {
dispatch({
type: "FETCH_USER_SUCCESS",
payload: response.data
});
})
.catch((err) => {
dispatch({
type: "FETCH_USER_ERROR",
payload: err
});
})
});
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
Im new to redux and I am into async now...
my application should load the users on page load but why my user state is returning undefined state? in the render method, i am logging the user props that contains the returned state from userReducer(mapStateToProps) but i am getting undefined. But when I getState() in store and log it, I am getting the expected result but it is not the ideal place to get the state right?... What i am doing wrong?
My store is:
import { createStore, applyMiddleware } from 'redux';
import logger from 'redux-logger';
import userReducer from './reducers/userReducer';
import thunk from 'redux-thunk';
const store = createStore(userReducer,applyMiddleware(thunk, logger()));
//store.subscribe(() => console.log('State: ',store.getState()));
export default store;
And my index is:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './container/App';
import { Provider } from 'react-redux';
import store from './store';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider> , document.getElementById('root'));
Also, on my default case in userReducer, what should I code there.. should I just break; or do I need also to return state it? Thank you
You should have;
const mapStateToProps = (state) => {
return {
user: state.users
};
};
mapStateToProps passes state (global store) as a props to the component.
Global store
const initUserState = {
fetching: false,
fetched: false,
users: [],
error: null
};
inside component listening to user state
const mapStateToProps = (state) => {
return {
user: state.users
};
};
Related
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);
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.
i used react 16+ and redux get jsonplaceholder fake data to assign posts state but not working. can't assign the state. how can i assign json values into state using concat method. i check lifecycle methods also but can't get the answer.
Reducer
import * as actiontypes from './actions';
import axios from 'axios';
const initalstate = {
counter: 0,
posts: []
};
const reducer = (state = initalstate, action ) => {
switch (action.type) {
case actiontypes.actionFetchPost:
axios.get('https://jsonplaceholder.typicode.com/posts')
.then(res => {
return {
...state,
posts: state.posts.concat(res.data)
}
});
break;
default :
return state;
}
};
export default reducer;
Redux reducers must be pure functions, it means they should not contain any side effects like calling api.
You need to call api in action creators using redux-thunk package.
Codesandbox
An example action creator:
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
}
};
};
And reducer file:
import {
FETCH_POSTS_STARTED,
FETCH_POSTS_SUCCESS,
FETCH_POSTS_FAILURE
} from "../actions/actionTypes";
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;
}
}
In store we use redux-thunk like this:
import { createStore, compose, applyMiddleware, combineReducers } from "redux";
import reduxThunk from "redux-thunk";
import postsReducers from "./reducers/postsReducers";
const rootReducer = combineReducers({
posts: postsReducers
});
const store = createStore(rootReducer, compose(applyMiddleware(reduxThunk)));
export default store;
Posts component:
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchPosts } from "./store/actions/postsActions";
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);
Index.js
import ReactDOM from "react-dom";
import store from "./store/store";
import { Provider } from "react-redux";
import Posts from "./Posts";
function App() {
return (
<div className="App">
<Posts />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
Do api call in action and return promise, use redux-thunk and redux-promise-middleware:
export const myApiCall = (args1, arg2) => async (dispatch, getState) => {
const payload = fetch({ ...config });
return dispatch({ type: 'MY_API_CALL', payload });
}
Then in reducer will have to handle two results:
MY_API_CALL_FULFILLED and MY_API_CALL_REJECTED
I want to get a response containing repositories from GitHub API and all I get is an empty array, this is my first time working with redux and redux thunk in react, this is my code:
App.js
import React from 'react';
import './App.css';
import Page from './components/layouts/page/index';
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
function App() {
return (
<Provider store={store}>
<Page />
</Provider>
);
}
export default App;
reducers/index.js
import { combineReducers } from 'redux';
import repos from './reducers';
const rootReducer = combineReducers({
repos,
});
export default rootReducer;
reducers/reducers.js
import { combineReducers } from 'redux';
const INITIAL_STATE = {
items: [],
isFetching: false,
error: undefined
};
function reposReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case 'FETCH_REPOS_REQUEST':
return Object.assign({}, state, {
isFetching: true
});
case 'FETCH_REPOS_SUCCESS':
return Object.assign({}, state, {
isFetching: false,
repos: action.repos
});
case 'FETCH_REPOS_FAILURE':
return Object.assign({}, state, {
isFetching: false,
error: action.error
});
default:
return state;
}
}
export default combineReducers({
repos: reposReducer
});
actions/actions.js
export function fetchRepos() {
return function(dispatch) {
dispatch({
type: 'FETCH_REPOS_REQUEST'
});
return fetch('https://api.github.com/search/repositories?q=sort=stars&order=desc')
.then(response => response.json().then(body => ({ response, body})))
.then(({ response, body }) => {
if (!response.ok) {
dispatch({
type: 'FETCH_REPOS_FAILURE',
error: body.error
});
} else {
dispatch({
type: 'FETCH_REPOS_SUCCESS',
repos: body.repos
});
}
}
);
}
}
pages.js
import React, { Component } from 'react';
import List from '../list/index.js';
import './page.scss';
import { connect } from 'react-redux';
import { fetchRepos } from '../../../actions/actions';
class Page extends Component {
componentDidMount() {
this.props.fetchRepos();
}
render() {
console.log(this.props);
return <div className="page"><List items={this.props.repos}/></div>
}
}
function mapDispatchToProps(dispatch) {
return {
fetchRepos: function() {
dispatch(fetchRepos());
}
};
}
function mapStateToProps(state) {
return {
repos: state.repos
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Page);
I want to get the response as an array and pass it as a prop to List component and manipulate it there! please let me know what I should change and what I'm not doing right, thanks for the help!!
The returned json from the server has body.items, you are trying to read it from body.repos.
Change your action to:
export function fetchRepos() {
return function(dispatch) {
dispatch({
type: "FETCH_REPOS_REQUEST",
});
return fetch(
"https://api.github.com/search/repositories?q=sort=stars&order=desc",
)
.then(response => response.json().then(body => ({response, body})))
.then(({response, body}) => {
if (!response.ok) {
dispatch({
type: "FETCH_REPOS_FAILURE",
error: body.error,
});
} else {
dispatch({
type: "FETCH_REPOS_SUCCESS",
repos: body.items,
});
}
});
};
}
I want to get a list of repositories from GitHub's API using redux in reactjs, but I get this error:
./src/components/layouts/page/index.js Line 14: Parsing error:
Unexpected token
function mapDispatchToProps(dispatch) {
^
return {
fetchRepos: function() {
dispatch(fetchRepos());
These are my files:
actions.js
export function fetchRepos() {
return function(dispatch) {
dispatch({
type: 'FETCH_REPOS_REQUEST'
});
return fetch('curl https://api.github.com/search/repositories?q=sort=stars&order=desc')
.then(response => response.json().then(body => ({ response, body })))
.then(({ response, body }) => {
if (!response.ok) {
dispatch({
type: 'FETCH_REPOS_FAILURE',
error: body.error
});
} else {
dispatch({
type: 'FETCH_REPOS_SUCCESS',
repos: body.repos
});
}
}
);
}
}
app.js
import React from 'react';
import './App.css';
import Page from './components/layouts/page/index';
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
function App() {
return (
<Provider store={store}>
<Page />
</Provider>
);
}
export default App;
reducers/index.js
import { combineReducers } from 'redux';
import repos from './reducers';
const rootReducer = combineReducers({
repos,
});
export default rootReducer;
reducers/reducers.js
import { combineReducers } from 'redux';
const INITIAL_STATE = {
items: [],
isFetching: false,
error: undefined
};
function reposReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case 'FETCH_REPOS_REQUEST':
return Object.assign({}, state, {
isFetching: true
});
case 'FETCH_REPOS_SUCCESS':
return Object.assign({}, state, {
isFetching: false,
repos: action.repos
});
case 'FETCH_REPOS_FAILURE':
return Object.assign({}, state, {
isFetching: false,
error: action.error
});
default:
return state;
}
}
export default combineReducers({
repos: reposReducer
});
Page.js
import React, { Component } from 'react';
import List from '../list/index.js';
import { connect } from 'react-redux';
import { fetchRepos } from '../../../actions/actions';
import './page.scss';
class Page extends Component {
componentDidMount() {
this.props.fetchRepos();
}
function mapDispatchToProps(dispatch) {
return {
fetchRepos: function() {
dispatch(fetchRepos());
}
};
}
function mapStateToProps(state) {
return {
repos: state.repos
};
}
render() {
return <List items={this.props.repos}/>
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Page);
Your mapDispatchToProps and mapStateToProps need to be defined outside of your component in order to be used by connect:
class Page extends Component {
componentDidMount() {
this.props.fetchRepos();
}
render() {
const { repos } = this.props;
return <List items={repos} />;
}
}
const mapDispatchToProps = dispatch => ({
fetchRepos: () => dispatch(fetchRepos())
});
const mapStateToProps = state => ({
repos: state.repos
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(Page);
For more information refer to this.
I am building a react app and implementing redux for data. When I navigate to a particular route, I want to dispatch the action to fetch the data from an external API and then once the data comes back, display the data for the user.
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 = {
marvel :{
characters: []
}
};
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;
reducer :
import * as constants from '../constants/constants';
const initialState = {
characters: [],
isFetching: false,
errorMessage: null
};
const marvelReducer = (state = initialState, action) => {
switch (action.type) {
case constants.FETCH_MARVEL :
return Object.assign({},state,{isFetching: true});
case constants.FETCH_MARVEL_SUCCESS :
return Object.assign({}. state,{
characters: [...action.response],
isFetching: false
});
case constants.FETCH_MARVEL_ERROR :
return Object.assign({}, state,{
isFetching: false,
errorMessage: action.message
});
default :
return state;
}
};
export default marvelReducer;
actions:
import 'whatwg-fetch';
import * as constants from '../constants/constants';
export const fetchMarvel = (dispatch) => {
const MARVEL_API = 'http://gateway.marvel.com:80/v1/public/characters?apikey=e542b1d89f93ed41b132eda89b9efb2c';
dispatch({
type: constants.FETCH_MARVEL
});
return fetch(MARVEL_API).then(
response => {
dispatch({
type: constants.FETCH_MARVEL_SUCCESS,
response
});
},
error => {
dispatch({
type: constants.FETCH_MARVEL_ERROR,
message: error.message || 'Something went wrong with fetchMarvel'
});
});
};
component :
import React, { Component } 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 {
render() {
const { characters, isFetching, errorMessage } = this.props;
return (
<div>
<h1>React Starter App</h1>
<h2>This is the home page</h2>
</div>
);
}
}
function mapStateToProps(state) {
return {
characters: state.characters,
isFetching: state.isFetching,
errorMessage: state.errorMessage
};
}
function mapDispatchToProps(dispatch) {
return { actions: bindActionCreators(actions, dispatch) };
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
I know I'm not currently displaying the props anywhere in the application, but first I am just trying to get them populated.
What step am I missing to dispatch the action so that I can populate the props from state?
You are not dispatching the action anywhere. So nothing happens.
You probably want to do this in a React lifecycle hook, for example:
class Home extends Component {
componentDidMount() {
this.props.actions.fetchMarvel();
}
render() {
const { characters, isFetching, errorMessage } = this.props;
return (
<div>
<h1>React Starter App</h1>
<h2>This is the home page</h2>
</div>
);
}
}