I'm learning redux and I'm trying to pull out values from state using useSelector hook and I really don't know why I cant see my error and loading property from state which is inside user obj. I'm also using initial state in store and when I try console log userInfo, error and loading I can see only userInfo and not loading and error. Is that initial state in store causing this problem? please help me out ..thank you
my code
login.js
import React, {useState, useEffect} from 'react';
import {useSelector, useDispatch} from 'react-redux';
import {loginUser, logoutUser} from '../../actions/userAction';
import {alert} from '../../actions/alertAction';
const Login = (props) => {
const dispatch = useDispatch();
const user= useSelector(state => state.user)
const alertMsg = useSelector(state => state.alert)
**console.log(user)**
**const {userInfo, loading, error} = user**
**console.log(userInfo, loading, error)**
return ("<h1>welcome to login page")
}
userAction.js file
import {USER_LOGIN_REQUEST, USER_LOGIN_SUCCESS,USER_LOGIN_ERROR, USER_REGISTER_REQUEST, USER_REGISTER_SUCCESS, USER_LOGOUT_SUCCESS} from '../types';
import axios from 'axios';
export const loginUser = (email, password) => async(dispatch) => {
try {
console.log('login user')
dispatch({
type: USER_LOGIN_REQUEST,
});
const config = {
headers: {
"Content-Type": "application/json",
},
};
const {data} = await axios.post(
"/user/login",
{ email, password },
config
);
dispatch({
type: USER_LOGIN_SUCCESS,
payload: data,
});
localStorage.setItem("userInfo", JSON.stringify(data));
}catch(error) {
console.log(error)
dispatch({
type: USER_LOGIN_ERROR,
payload: error.response.data.msg
})
}
}
userReducer.js file
import {USER_LOGIN_REQUEST, USER_LOGIN_SUCCESS, USER_REGISTER_REQUEST,USER_LOGIN_ERROR, USER_REGISTER_SUCCESS, USER_LOGOUT_SUCCESS} from '../types';
export default (state = {}, action) => {
switch (action.type) {
case USER_LOGIN_REQUEST:
return {
...state,
user: {loading: true}
};
case USER_LOGIN_SUCCESS:
return {
...state,
user: {
userInfo: action.payload, loading: false
}
}
case USER_LOGIN_ERROR:
return {
...state,
user: {
loading: false, error: action.payload
}
}
case USER_LOGOUT_SUCCESS:
return {
...state
};
default:
return state
}
}
index.js file
import {combineReducers} from 'redux';
import cartReducer from './cartReducer';
import userReducer from './userReducer';
export default combineReducers({
cart: cartReducer,
user: userReducer
})
store.js file
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index.js';
const cartItemsFromStorage = localStorage.getItem('cartItems') ? JSON.parse(localStorage.getItem('cartItems')) : []
const userInfoFromStorage = localStorage.getItem('userInfo') ? JSON.parse(localStorage.getItem('userInfo')) : null
const initialState = {
**cart: {
cartItems: cartItemsFromStorage
},
user: {
userInfo: userInfoFromStorage
}**
};
const middleware = [thunk];
const store = createStore(rootReducer, initialState, composeWithDevTools(applyMiddleware(...middleware)));
export default store;
Problem is in how you are storing data in redux.
case USER_LOGIN_REQUEST:
return {
...state,
loading: true, // Here you are storing loading in redux state directly
error: null // same as loading
};
To solve this you need to store like below:-
case USER_LOGIN_REQUEST:
return {
...state,
user: { ...state.user, loading: true, error: null}
};
You also need to change this for all your cases where you are trying to store loading and error in user
Related
Im new to react and redux. After following a tutorial and doing everything wrong (he was using react-router-dom 5.2 and I was using 6) I decided to do it like him and use react-router-dom 5.2. Now for some reason the redux part of the project doesn't work. I don't even get an error. It just doesn't show in the redux dev tools. I think the frontend isn't connecting to the backend properly (Django). Maybe someone has an idea?
Homescreen:
import React, { useState, useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Row, Col } from 'react-bootstrap'
import Product from '../components/Product'
import Loader from '../components/Loader'
import Message from '../components/Message'
import { listProducts } from '../actions/productActions'
function HomeScreen() {
const dispatch = useDispatch()
const productList = useSelector(state => state.productList)
const { error, loading, products} = productList
//let keyword = history.location.search
useEffect(() => {
dispatch(listProducts())
}, [dispatch])
productReducers.js
import {
PRODUCT_LIST_REQUEST,
PRODUCT_LIST_SUCCESS,
PRODUCT_LIST_FAIL,
PRODUCT_DETAILS_REQUEST,
PRODUCT_DETAILS_SUCCESS,
PRODUCT_DETAILS_FAIL,
} from '../constants/productConstants'
export const productListReducer = (state = { products: [] }, action) => {
switch (action.type) {
case PRODUCT_LIST_REQUEST:
return { loading: true, products: [] }
case PRODUCT_LIST_SUCCESS:
return {
loading: false,
products: action.payload
}
case PRODUCT_LIST_FAIL:
return { loading: false, error: action.payload }
default:
return state
}
}
productActions.js
import axios from 'axios'
import {
PRODUCT_LIST_REQUEST,
PRODUCT_LIST_SUCCESS,
PRODUCT_LIST_FAIL,
PRODUCT_DETAILS_REQUEST,
PRODUCT_DETAILS_SUCCESS,
PRODUCT_DETAILS_FAIL,
} from '../constants/productConstants'
export const listProducts = () => async (dispatch) => {
try {
dispatch({ type: PRODUCT_LIST_REQUEST })
const { data } = await axios.get('/api/products/')
dispatch({
type: PRODUCT_LIST_SUCCESS,
payload: data
})
} catch (error) {
dispatch({
type: PRODUCT_LIST_FAIL,
payload: error.response && error.response.data.detail
? error.response.data.detail
: error.message,
})
}
}
store.js
import { legacy_createStore as createStore, combineReducers, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
import {productListReducer} from './reducers/productReducers'
const reducer = combineReducers({
productList: productListReducer,
})
const initialState = {}
const middleware = [thunk]
const store = createStore(reducer, initialState,
composeWithDevTools(applyMiddleware(...middleware)))
export default store
My redux dev tool in browser is empty and there are no errors on the console. Thank you!
I am facing this error when I dispatch the action. I have used this method before it worked fine this time it constantly shows this error. Please Help I am unable to find any solutions on this. Thanks in advance.
Action:
export const listPackages = () => async (dispatch) => {
try {
dispatch({ type: PACKAGE_LIST_REQUEST })
const {data} = await axios.get('/api/packages/all')
dispatch({
type: PACKAGE_LIST_SUCCESS,
payload: data
})
} catch (error) {
dispatch({
type: PACKAGE_LIST_FAIL,
payload: error.message && error.response.data.message ? error.response.data.message : error.message
})
}
}
My Reducer:
import {
PACKAGE_LIST_REQUEST,
PACKAGE_LIST_SUCCESS,
PACKAGE_LIST_FAIL
} from "../constants/packageConstants"
export const packageListReducer = (state = {packages: []}, action) => {
switch(action.type){
case PACKAGE_LIST_REQUEST:
return {loading: true, packages: []}
case PACKAGE_LIST_SUCCESS:
return {loading: false, packages: action.payload}
case PACKAGE_LIST_FAIL:
return {loading: false, error: action.payload}
default:
return state
}
}
My Store:
import {createStore, combineReducers, applyMiddleware} from "redux"
import thunk from "redux-thunk"
import {composeWithDevTools} from "redux-devtools-extension"
import {packageListReducer} from "./reducers/packageReducers";
const reducer = combineReducers({
packageList: packageListReducer,
})
const initialState = {}
const middleware = [thunk]
const store = createStore(reducer,initialState,composeWithDevTools(applyMiddleware(...middleware)))
export default store
Component From Where I dispatched the action:
import {listPackages} from "../actions/packageActions"
export default function HomeScreen1() {
const dispatch = useDispatch()
const packages = []
useEffect(() => {
dispatch(listPackages())
}, [dispatch])
I'm using Redux for the first time and I'm getting this error on the redux devtools:
error: "Actions must be plain objects. Use custom middleware for async actions."
This is my store.js code:
import {createStore, combineReducers, applyMiddleware, compose} from 'redux';
import thunk from 'redux-thunk';
import {productListReducer} from './reducers/productReducers';
const initialState = {};
const reducer = combineReducers ( {
productList: productListReducer,
})
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, initialState, composeEnhancer(applyMiddleware(thunk)));
export default store;
And the productReducers.js where I have the switch for PRODUCT_LIST_FAIL:
import { PRODUCT_LIST_REQUEST, PRODUCT_LIST_SUCCESS, PRODUCT_LIST_FAIL } from "../constants/productConstants";
function productListReducer(state = {products:[]}, action) {
switch (action.type) {
case PRODUCT_LIST_REQUEST:
return {loading: true};
case PRODUCT_LIST_SUCCESS:
return {loading: false, products: action.payload};
case PRODUCT_LIST_FAIL:
return {loading: false, error: action.payload};
default:
return state;
}
}
export {productListReducer};
The import from the last code comes from here (productConstants.js):
export const PRODUCT_LIST_REQUEST = 'PRODUCT_LIST_REQUEST';
export const PRODUCT_LIST_SUCCESS = 'PRODUCT_LIST_SUCCESS';
export const PRODUCT_LIST_FAIL = 'PRODUCT_LIST_FAIL';
productActions.js:
import axios from 'axios';
import {
PRODUCT_LIST_REQUEST,
PRODUCT_LIST_SUCCESS,
PRODUCT_LIST_FAIL,
} from '../constants/productConstants';
const listProducts = () => async(dispatch) => {
try {
dispatch(PRODUCT_LIST_REQUEST);
const {data} = await axios.get("/api/products");
dispatch({type: PRODUCT_LIST_SUCCESS, payload: data});
}
catch(error) {
dispatch({type: PRODUCT_LIST_FAIL, payload: error.message});
}
}
export {listProducts}
guys.
My bad, I was writing dispatch(PRODUCT_LIST_REQUEST); instead of dispatch({type: PRODUCT_LIST_REQUEST}); inside the try-catch in productActions.js, that's why the PRODUCT_LIST_REQUEST was assumed as a string and not an object...
I'm following this tutorial (https://www.udemy.com/course/mern-stack-front-to-back/) and build a mern website step by step. Everything went well until "Profile Reducer & Get Current Profile". I added actions/profile.js and reducers/profile.js, etc. And I got the following error.
I think my code is the same as what the video taught. Does anyone know what may be the problem?
reducers/profile.js:
import {
GET_PROFILE,
PROFILE_ERROR
} from "../actions/types";
const initialState = {
profile: null,
profiles: [],
repos: [],
loading: true,
error: {}
};
export default function(state = initialState, action) {
const { type, payload } = action;
switch (type) {
case GET_PROFILE:
return {
...state,
profile: payload,
loading: false
};
case PROFILE_ERROR:
return {
...state,
error: payload,
loading: false
};
default:
return state
}
}
actions/profile.js
import axios from "axios";
import { setAlert } from "./alert";
import { GET_PROFILE, PROFILE_ERROR } from "./types";
import { response } from "express";
// Get current users profile
export const getCurrentProfile = () => async dispatch => {
try {
const res = await axios.get("/api/profile/me");
dispatch({
type: GET_PROFILE,
payload: res.data
});
} catch (err) {
dispatch({
type: PROFILE_ERROR,
payload: { msg: err.response.statusText, status: err.response.status }
});
}
};
Dashboard.js:
import React, { useEffect } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { getCurrentProfile } from "../../actions/profile";
const Dashboard = ({ getCurrentProfile, auth, profile }) => {
useEffect(() => {
getCurrentProfile();
}, []);
return <div>Dashboard</div>;
};
Dashboard.propTypes = {
getCurrentProfile: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
profile: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
auth: state.auth,
profile: state.profile
});
export default connect(mapStateToProps, { getCurrentProfile })(Dashboard);
Judging from the stack trace in your screenshot, the issue appears to be somewhere in relation to the express package.
From the code snippets you've submitted as part of the question, only the file: actions/profile.js contains any code in relation to express. It contains the following import statement:
import { response } from "express";
I don't see where you're using response, so I assume you don't need it - which is where you admitted the issue was coming from in the comments after you removed that line of code.
I am starting with ReactJS and Redux and last few days, I am being stuck on a problem when I leave my app open in the browser for a while and then got back to it, I see there this error:
TypeError: Cannot read property 'push' of undefined
It's here, in my Event.js component:
import React, { Component } from 'react';
import axios from 'axios';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { registerUser, logoutUser } from '../redux/actions/authentication';
import { withRouter } from 'react-router-dom';
class Event extends Component {
constructor() {
super();
this.state = {
...
}
UNSAFE_componentWillMount() {
if(!this.props.auth.isAuthenticated) {
console.log('Unauthorized - Event action');
this.props.history.push('/');
}
}
componentDidMount() {
axios.get('/api/events')
.then((response) => {
this.setState({events: response.data});
console.log('events: ', this.state.events);
}).catch(err => {
console.log('CAUGHT IT! -> ', err);
});
}
componentWillReceiveProps(nextProps) {
if(nextProps.errors) {
this.setState({
errors: nextProps.errors
});
}
}
...
render() {
const { errors } = this.state;
const {isAuthenticated, user} = this.props.auth;
return (...)
}
Event.propTypes = {
registerUser: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
auth: state.auth,
errors: state.errors
});
export default connect(mapStateToProps,{ registerUser })(withRouter(Event))
Then, my redux/actions/authentication.js looks like this:
import axios from 'axios';
import { GET_ERRORS, SET_CURRENT_USER } from './types'; // we list here the actions we'll use
import setAuthToken from '../../setAuthToken';
import jwt_decode from 'jwt-decode';
export const registerUser = (user, history) => dispatch => {
axios.post('/api/users/register', user)
.then(res => history.push('/login'))
.catch(err => {
dispatch({
type: GET_ERRORS,
payload: err.response.data
});
});
}
export const loginUser = (user) => dispatch => {
axios.post('/api/users/login', user)
.then(res => {
//console.log(res.data);
const { token } = res.data;
localStorage.setItem('jwtToken', token);
setAuthToken(token);
const decoded = jwt_decode(token);
dispatch(setCurrentUser(decoded));
})
.catch(err => {
dispatch({
type: GET_ERRORS,
payload: err.response.data
});
});
}
export const setCurrentUser = decoded => {
return {
type: SET_CURRENT_USER,
payload: decoded
}
}
export const logoutUser = (history) => dispatch => {
localStorage.removeItem('jwtToken');
setAuthToken(false);
dispatch(setCurrentUser({}));
history.push('/login');
}
And reducers - authReducer.js:
import { SET_CURRENT_USER } from '../actions/types';
import isEmpty from '../../validation/is-empty';
const initialState = {
isAuthenticated: false,
user: {}
}
export default function(state = initialState, action) {
switch(action.type) {
case SET_CURRENT_USER:
return {
...state,
isAuthenticated: !isEmpty(action.payload),
user: action.payload
}
default:
return state;
}
}
errorReducer.js goes like this:
import { GET_ERRORS } from '../actions/types';
const initialState = {};
export default function(state = initialState, action ) {
switch(action.type) {
case GET_ERRORS:
return action.payload;
default:
return state;
}
}
and index.js:
import { combineReducers } from 'redux';
import errorReducer from './errorReducer';
import authReducer from './authReducer';
export default combineReducers({
errors: errorReducer,
auth: authReducer
});
In the nabber menu, I have a link to log out the user. If the user clicks the link, I log him out like this:
onLogout(e) {
e.preventDefault();
this.props.logoutUser(this.props.history);
}
However, I am still unable to figure out why I am seeing the error above. What I also don't understand here is that when I get that error screen and then refresh the page, the error page disappears and I am redirected from localhost:3000/events to localhost:3000.
You should use
withRouter(connect(...)(MyComponent))
and not
connect(...)(withRouter(MyComponent))
here is the documentation
I think this.props.history is undefined in your example because of this.
Make sure your the object you pass to the logoutUser function is not undefined and the history parameter is received in the right way. You are trying to invoke the push method on the history object, but in this case it tells you that the function can not be found because history is undefined. Hope this helps.