I am using Redux API Middleware to call the api for the data
Action.js
import { CALL_API } from 'redux-api-middleware';
export const FETCH_POSTS = 'FETCH_POSTS';
export const FETCH_POSTS_SUCCESS = 'FETCH_POSTS_SUCCESS';
export const FETCH_POSTS_FAILURE = 'FETCH_POSTS_FAILURE';
export const fetchPosts = () => ({
[CALL_API]: {
types: [
{
type: FETCH_POSTS,
payload: (action, state) => ({ action: state })
},
{
type: FETCH_POSTS_SUCCESS,
payload: (action, state, response) => {
return response
}
},
FETCH_POSTS_FAILURE
],
endpoint: 'http://localhost:8080/v1/career/',
method: 'GET',
}
});
Reducer.js
import {
FETCH_POSTS,
FETCH_POSTS_SUCCESS,
FETCH_POSTS_FAILURE
} from '../actions/Action';
import {combineReducers} from 'redux'
const INITIAL_STATE = { postsList: { posts: [], error: null, loading: false } };
export const posts=(state = INITIAL_STATE, action)=> {
let error;
switch(action.type) {
case FETCH_POSTS:
return { ...state, postsList: { posts: [], error: null, loading: true} };
case FETCH_POSTS_SUCCESS:
return { ...state, postsList: { posts: action.payload, error: null, loading: false } };
case FETCH_POSTS_FAILURE:
error = action.payload.data || { message: action.payload.message };
return { ...state, postsList: { posts: [], error: error, loading: false } };
default:
return state;
}
export const reducers=combineReducers({
posts:posts
});
export default reducers;
Store.js
import {
applyMiddleware,
createStore,compose
} from 'redux';
import { apiMiddleware } from 'redux-api-middleware';
import reducers from './reducer'
export function ConfigureStore(IntitialState={}){
const stores=createStore(reducers,IntitialState,compose(
applyMiddleware(apiMiddleware),
window.devToolsExtension ? window.devToolsExtension() : f => f
));
return stores;
};
export const store=ConfigureStore()
I can see only the FETCH POSTS is running and when i check the state i get this
I checked the network section of the developer tools.The response is coming from the server
and my response is
I dont know why this not working.Please some one help me .Thanks.
Related
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
So my reducers are not updating the state for my app. The initial state in user shows up with no changes made. The initial state from uiReducer doesn't come up at all. emphasized textI can log in okay and receive a token, so I think it is a problem with getUserData. I have no idea why the ui initial state isn't working however. This is my first time trying to use react redux so I probably have a dumb mistake somewhere. Any help appreciated!
EDIT: Without changing code, I just restarted my app and now both of the initial states are loading, but user is still not populated with data.
login
import React, { Component } from 'react';
import PropTypes from 'prop-types'
// redux
import {connect} from 'react-redux'
import {loginUser} from '../redux/actions/userActions'
class login extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
errors: [],
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.UI.errors) {
this.setState({
errors: nextProps.UI.errors
});
}
}
handleChange = (event) => {
this.setState({
[event.target.name]: event.target.value
});
};
handleSubmit = (event) => {
event.preventDefault();
const userData = {
email: this.state.email,
password: this.state.password
};
this.props.loginUser(userData, this.props.history)
};
render() {
const { classes, UI: {loading} } = this.props;
const { errors } = this.state;
return (
..............
);
}
}
login.propTypes = {
classes: PropTypes.object.isRequired,
loginUser: PropTypes.func.isRequired,
user: PropTypes.object.isRequired,
UI:PropTypes.object.isRequired
}
const mapStateToProps = (state) => ({
user: state.user,
UI: state.UI
})
const mapActionsToProps = {
loginUser
}
export default connect(mapStateToProps, mapActionsToProps)(withStyles(styles)(login));
userActions
import axios from 'axios';
export const loginUser = (userData, history) => (dispatch) => {
dispatch({ type: 'LOADING_UI' });
axios
.post('/login', userData)
.then((res) => {
setAuthorizationHeader(res.data.token);
dispatch(getUserData())
dispatch({type: 'CLEAR_ERRORS'})
history.push('/');
})
.catch((error) => {
dispatch({
type: 'SET_ERRORS',
payload: error.response.data
});
});
}
export const getUserData = ()=> (dispatch) => {
axios
.get('/user')
.then(res => {
dispatch({
type: 'SET_USER',
payload: res.data
})
})
}
const setAuthorizationHeader = (token) => {
const FBIdToken = `Bearer ${token}`;
localStorage.setItem('FBIdToken', FBIdToken);
axios.defaults.headers.common['Authorization'] = FBIdToken;
};
userReducer
const initialState = {
authenticated: false,
credentials: {},
approves: []
}
export default function(state = initialState, action){
switch(action.type){
case 'SET_AUTHENTICATED':
return{
...state,
authenticated: true
}
case 'SET_UNAUTHENTICATED':
return initialState
case 'SET_USER':
return{
authenticated: true,
loading: false,
...action.payload
}
default:
return state
}
}
uiReducer
const initialState = {
loading: false,
errors: null
}
export default function(state = initialState, action) {
switch (action.type) {
case 'SET_ERRORS':
return {
...state,
loading: false,
errors: action.payload
};
case 'CLEAR_ERRORS':
return {
...state,
loading: false,
errors: null
};
case 'LOADING_UI':
return {
...state,
loading: true
};
default:
return state
}
}
I'm really confused that I can log in fine, but the state isnt updated, and only the user state is initialized..
EDIT:
store.js
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import userReducer from './reducers/userReducer';
import dataReducer from './reducers/dataReducer';
import uiReducer from './reducers/uiReducer';
const initialState = {};
const middleware = [thunk];
const reducers = combineReducers({
user: userReducer,
data: dataReducer,
UI: uiReducer
});
const store = createStore(reducers,
initialState,
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
export default store;
Changing ...action.payload to user: action.payload seems to have fixed it. I got the code for this section from a tutorial but the syntax must have been slightly outdated or something, who knows
export default function(state = initialState, action){
switch(action.type){
case 'SET_AUTHENTICATED':
return{
...state,
authenticated: true
}
case 'SET_UNAUTHENTICATED':
return initialState
case 'SET_USER':
return{
authenticated: true,
loading: false,
user: action.payload
}
default:
return state
}
}
isAuthenticated is undefined when i run this code. how can is use isAuthenticated with mapStateProps. if i am use Token `(Token '5302f4340a76cd80a855286c6d9e0e48d2f519cb'} like this then it's working fine but i want Authorized it with props.isAuthenticated anybody know how can i solve this issue?
authAction.js
import axios from 'axios';
import * as actionTypes from './actionTypes';
export const authStart = () => {
return {
type: actionTypes.AUTH_START
}
}
export const authSuccess = token => {
return {
type: actionTypes.AUTH_SUCCESS,
token: token
}
}
export const authFail = error => {
return {
type: actionTypes.AUTH_FAIL,
error: error
}
}
export const logout = () => {
localStorage.removeItem('token');
return {
type: actionTypes.AUTH_LOGOUT
};
}
export const authLogin = (userData) => {
return dispatch => {
dispatch(authStart());
axios.post('http://localhost:8000/rest-auth/login/', userData)
.then(res => {
const token = res.data.key;
localStorage.setItem('token', token);
dispatch(authSuccess(token));
})
.catch(err => {
dispatch(authFail(err))
})
}
}
authReducer.js
import * as actionTypes from '../actions/actionTypes';
import { updateObject } from '../utility';
const initialState = {
isAuthenticated: null,
token: null,
error: null,
loading: false
}
const authStart = (state, action) => {
return updateObject(state, {
isAuthenticated: false,
error: null,
loading: true
});
}
const authSuccess = (state, action) => {
return updateObject(state, {
isAuthenticated: true,
token: action.token,
error: null,
loading: false
});
}
const authFail = (state, action) => {
return updateObject(state, {
error: action.error,
loading: false
});
}
const authLogout = (state, action) => {
return updateObject(state, {
token: null
});
}
export default function (state = initialState, action) {
switch (action.type) {
case actionTypes.AUTH_START: return authStart(state, action);
case actionTypes.AUTH_SUCCESS: return authSuccess(state, action);
case actionTypes.AUTH_FAIL: return authFail(state, action);
case actionTypes.AUTH_LOGOUT: return authLogout(state, action);
default:
return state;
}
}
index.js
import { combineReducers } from 'redux';
import auth from './authReducer'
export default combineReducers({
auth: auth
});
articleList.js
const NewsList = (props) => {
// ...
const fetchItems = async () => {
try {
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Token ${props.isAuthenticated}`
}
}
const res = await axios.get(`${process.env.REACT_APP_API_URL}/api/`, config);
setItems(res.data)
setLoading(false);
}
catch (err) {
console.log(`😱 Axios request failed: ${err}`);
}
}
fetchItems()
})
}, [items]);
// ...
}
const mapStateToProps = (state) => {
return {
isAuthenticated: state.auth.token
}
}
export default connect(mapStateToProps)(NewsList)
You need to debug your code. Start by connecting the dots: The output tells you that props.isAuthenticated is undefined. You pass this in from state.auth.token in mapStateToProps():
const mapStateToProps = (state) => {
return {
isAuthenticated: state.auth.token
}
}
So state.auth.token must be undefined also. That's as far as I can get from what you have shown me. You will need to debug further to figure out why. You can use the React Dev Tools to inspect props of your components. You can use Redux Dev Tools to inspect and manipulate the redux state. Check what the value of auth.token is in state. Look where it is supposed to be set and find out why it isn't getting set to a valid value.
Be sure to check this article for tips on how to debug your code.
I'm trying to create a function add product into cart with redux-react
and how can I get my product info from mongoDB into initialState?
this is how my product info looks like:
img_url1: "https://thebeuter.com/wp-content/uploads/2020/06/38-1.jpg"
price: 1290000
title: "BEUTER BACK2BACK ZIPPER WHITE JACKET"
here is my reducer:
import {
ADD_PRODUCT_BASKET,
GET_NUMBERS_BASKET
} from '../actions/type'
const initialState = {
basketNumbers: 0,
cartCost: 0,
products: {
}
}
export default (state = initialState, action) => {
switch (action.type) {
case ADD_PRODUCT_BASKET:
let addQuantity = {
...state.products[action.payload]
}
console.log(addQuantity)
return {
...state,
basketNumbers: state.basketNumbers + 1,
};
case GET_NUMBERS_BASKET:
return {
...state
};
default:
return state;
}
}
Here is my github if you want to look at my code:
https://github.com/nathannewyen/the-beuter
You solve your problem using redux-saga (or redux-thunk) by fetching your data from DB before rendering your page:
productBasket.js (with redux-saga)
import axios from 'axios';
import { action as createAction } from 'typesafe-actions';
import {
put, select, takeLatest,
} from 'redux-saga/effects';
export const FETCH_PRODUCT_BASKET = 'FETCH_PRODUCT_BASKET';
export const FETCH_PRODUCT_BASKET_SUCCESS = 'FETCH_PRODUCT_BASKET_SUCCESS';
export const FETCH_PRODUCT_BASKET_ERROR = 'FETCH_PRODUCT_BASKET_ERROR';
export const actionCreators = {
fetchProductBasket: () =>
createAction(FETCH_PRODUCT_BASKET),
fetchProductBasketSuccess: (products) =>
createAction(FETCH_PRODUCT_BASKET_SUCCESS, { products }),
fetchProductBasketError: (error) =>
createAction(FETCH_PRODUCT_BASKET_ERROR, { error }),
};
export const {
fetchProductBasket,
fetchProductBasketSuccess,
fetchProductBasketError
} = actionCreators;
export const initialState = {
isFetching: false,
isError: false,
basketNumber: 0,
products: []
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_PRODUCT_BASKET:
return {
...state,
isFetching: true,
isError: false
}
case FETCH_PRODUCT_BASKET_SUCCESS:
return {
...state,
isFetching: false,
isError: false,
products: action.payload.products
}
case FETCH_PRODUCT_BASKET_ERROR:
return {
...state,
isFetching: false,
isError: true
}
default:
return state;
}
}
export default reducer;
export function* basketSaga() {
yield takeLatest(FETCH_PRODUCT_BASKET, fetchProductBasketSaga);
}
function* fetchProductBasketSaga() {
try {
// here is code with fetching data
const { data } = yield axios.get('some address');
yield put(fetchProductBasketSuccess(data));
} catch (err) {
console.log(err);
yield put(fetchProductBasketError(err));
}
}
And after that dispatch fetchProductBasket action in useEffect scope in your component. You can show skeleton to user while your data is fetching.
I am building a Jhipster React generated project. My problem is I couldn't manage to chain reducer functions.
Simply, I want to chain getSession() function with another function in the authentication reducer.
In my component I want to handle then() operation like getSession().then(....
Can you please help for this?
Here is the authentication.ts reducer:
authentication.ts
import axios from 'axios';
import { Storage } from 'react-jhipster';
import { REQUEST, SUCCESS, FAILURE } from 'app/shared/reducers/action-type.util';
export const ACTION_TYPES = {
LOGIN: 'authentication/LOGIN',
GET_SESSION: 'authentication/GET_SESSION',
LOGOUT: 'authentication/LOGOUT',
CLEAR_AUTH: 'authentication/CLEAR_AUTH',
ERROR_MESSAGE: 'authentication/ERROR_MESSAGE'
};
const AUTH_TOKEN_KEY = 'jhi-authenticationToken';
const initialState = {
loading: false,
isAuthenticated: false,
loginSuccess: false,
loginError: false, // Errors returned from server side
showModalLogin: false,
account: {} as any,
errorMessage: null as string, // Errors returned from server side
redirectMessage: null as string
};
export type AuthenticationState = Readonly<typeof initialState>;
// Reducer
export default ( state: AuthenticationState = initialState, action ): AuthenticationState => {
switch ( action.type ) {
case REQUEST( ACTION_TYPES.LOGIN ):
case REQUEST( ACTION_TYPES.GET_SESSION ):
return {
...state,
loading: true
};
case FAILURE( ACTION_TYPES.LOGIN ):
return {
...initialState,
errorMessage: action.payload,
showModalLogin: true,
loginError: true
};
case FAILURE( ACTION_TYPES.GET_SESSION ):
return {
...state,
loading: false,
isAuthenticated: false,
showModalLogin: true,
errorMessage: action.payload
};
case SUCCESS( ACTION_TYPES.LOGIN ):
return {
...state,
loading: false,
loginError: false,
showModalLogin: false,
loginSuccess: true
};
case ACTION_TYPES.LOGOUT:
return {
...initialState,
showModalLogin: true
};
case SUCCESS( ACTION_TYPES.GET_SESSION ): {
const isAuthenticated = action.payload && action.payload.data && action.payload.data.activated;
return {
...state,
isAuthenticated,
loading: false,
account: action.payload.data
};
}
case ACTION_TYPES.ERROR_MESSAGE:
return {
...initialState,
showModalLogin: true,
redirectMessage: action.message
};
case ACTION_TYPES.CLEAR_AUTH:
return {
...state,
loading: false,
showModalLogin: true,
isAuthenticated: false
};
default:
return state;
}
};
export const displayAuthError = message => ( { type: ACTION_TYPES.ERROR_MESSAGE, message } );
export const getSession = () => dispatch => {
dispatch( {
type: ACTION_TYPES.GET_SESSION,
payload: axios.get( '/api/account' )
} );
};
export const login = ( username, password, rememberMe = false ) => async ( dispatch, getState ) => {
const result = await dispatch( {
type: ACTION_TYPES.LOGIN,
payload: axios.post( '/api/authenticate', { username, password, rememberMe } )
} );
const bearerToken = result.value.headers.authorization;
if ( bearerToken && bearerToken.slice( 0, 7 ) === 'Bearer ' ) {
const jwt = bearerToken.slice( 7, bearerToken.length );
if ( rememberMe ) {
Storage.local.set( AUTH_TOKEN_KEY, jwt );
} else {
Storage.session.set( AUTH_TOKEN_KEY, jwt );
}
}
dispatch( getSession() );
};
export const clearAuthToken = () => {
if ( Storage.local.get( AUTH_TOKEN_KEY ) ) {
Storage.local.remove( AUTH_TOKEN_KEY );
}
if ( Storage.session.get( AUTH_TOKEN_KEY ) ) {
Storage.session.remove( AUTH_TOKEN_KEY );
}
};
export const logout = () => dispatch => {
clearAuthToken();
dispatch( {
type: ACTION_TYPES.LOGOUT
} );
};
export const clearAuthentication = messageKey => ( dispatch, getState ) => {
clearAuthToken();
dispatch( displayAuthError( messageKey ) );
dispatch( {
type: ACTION_TYPES.CLEAR_AUTH
} );
};
store.ts
import { createStore, applyMiddleware, compose } from 'redux';
import promiseMiddleware from 'redux-promise-middleware';
import thunkMiddleware from 'redux-thunk';
import reducer, { IRootState } from 'app/shared/reducers';
import DevTools from './devtools';
import errorMiddleware from './error-middleware';
import notificationMiddleware from './notification-middleware';
import loggerMiddleware from './logger-middleware';
import { loadingBarMiddleware } from 'react-redux-loading-bar';
const defaultMiddlewares = [
thunkMiddleware,
errorMiddleware,
notificationMiddleware,
promiseMiddleware(),
loadingBarMiddleware(),
loggerMiddleware
];
const composedMiddlewares = middlewares =>
process.env.NODE_ENV === 'development'
? compose(
applyMiddleware(...defaultMiddlewares, ...middlewares),
DevTools.instrument()
)
: compose(applyMiddleware(...defaultMiddlewares, ...middlewares));
const initialize = (initialState?: IRootState, middlewares = []) => createStore(reducer, initialState, composedMiddlewares(middlewares));
export default initialize;
Home Component
import './home.css';
import React from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { Button, Row, Col, Alert, Table } from 'reactstrap';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { IRootState } from 'app/shared/reducers';
import { getSession } from 'app/shared/reducers/authentication';
import { getWhoIsInsideInfo, exitVisitor } from 'app/entities/visitor/visitor.reducer';
import { IVisitorInsideInfo } from 'app/shared/model/visitors_inside.model';
import { TextFormat } from 'react-jhipster';
import { APP_TIMESTAMP_FORMAT } from 'app/config/constants';
import VisitorDeleteDialog from 'app/entities/visitor/visitor-delete-dialog';
export interface IHomeProp extends StateProps, DispatchProps { }
const mapStateToProps = ( { authentication, visitorsInsideInfo }: IRootState ) => ( {
account: authentication.account,
isAuthenticated: authentication.isAuthenticated,
visitorInsideList: visitorsInsideInfo.insiderEntities,
loginSuccess: authentication.loginSuccess
} );
type StateProps = ReturnType<typeof mapStateToProps>;
type DispatchProps = typeof mapDispatchToProps;
const mapDispatchToProps = { getSession, getWhoIsInsideInfo, exitVisitor };
export class Home extends React.Component<IHomeProp> {
interval: any;
orgDispatch: any;
constructor( props ) {
super( props );
this.renderVisitorsInside = this.renderVisitorsInside.bind( this );
}
setListTimer() {
console.log( 'DIS' );
}
getList = () => {
if ( this.props.account && this.props.account.login ) {
getWhoIsInsideInfo();
}
};
.....
}
In my Home component I want to first call getSession then call getList. If response was OK else reject it.
Thanks from now on.
It looks like you have everything right. You just need to call your reducer functions with dispatch.
For example:
dispatch(getSession()).then(...)