JonasLevin
JonasLevin

Reputation: 2089

React: Access Data from Redux in component

I have created an authentification system in react with redux and axios but I can`t figure out how to render the data in my components.
This is my actions/auth.js:

import axios from 'axios';
import {
    SIGNUP_SUCCESS,
    SIGNUP_FAIL,
    LOGIN_SUCCESS,
    LOGIN_FAIL,
    ACTIVATION_SUCCESS,
    ACTIVATION_FAIL,
    RESET_PASSWORD_SUCCESS,
    RESET_PASSWORD_FAIL,
    RESET_PASSWORD_CONFIRM_SUCCESS,
    RESET_PASSWORD_CONFIRM_FAIL,
    LOGOUT,
    USER_LOADED_SUCCESS,
    USER_LOADED_FAIL,
    AUTHENTICATED_FAIL,
    AUTHENTICATED_SUCCESS
} from './types';

export const checkAuthenticated = () => async dispatch => {
    if (typeof window == 'undefined') {
        dispatch({
            type: AUTHENTICATED_FAIL
        });
    }
    if (localStorage.getItem('access')) {
        const config = {
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            }
        };
    
        const body = JSON.stringify({ token: localStorage.getItem('access') });
    
        try {
            const res = await axios.post(`${process.env.REACT_APP_API_URL}/auth/jwt/verify/`, body, config);
    
            if (res.data.code !== 'token_not_valid') {
                dispatch({
                    type: AUTHENTICATED_SUCCESS
                });
            } else {
                dispatch({
                    type: AUTHENTICATED_FAIL
                });
            }
        } catch (err) {
            dispatch({
                type: AUTHENTICATED_FAIL
            });
        }
    } else {
        dispatch({
            type: AUTHENTICATED_FAIL
        });
    }
};

export const load_user = () => async dispatch => {
    if (localStorage.getItem('access')) {
        const config = {
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `JWT ${localStorage.getItem('access')}`,
                'Accept': 'application/json'
            }
        };

        try {
            const res = await axios.get(`${process.env.REACT_APP_API_URL}/auth/users/me/`, config);

            dispatch({
                type: USER_LOADED_SUCCESS,
                payload: res.data
            });
        } catch (err) {
            dispatch({
                type: USER_LOADED_FAIL
            });
        }
    } else {
        dispatch({
            type: USER_LOADED_FAIL
        });
    }
};

export const login = (email, password) => async dispatch => {
    const config = {
        headers: {
            'Content-Type': 'application/json'
        }
    };

    const body = JSON.stringify({ email, password });

    try {
        const res = await axios.post(`${process.env.REACT_APP_API_URL}/auth/jwt/create/`, body, config);

        dispatch({
            type: LOGIN_SUCCESS,
            payload: res.data
        });

        dispatch(load_user());
    } catch (err) {
        dispatch({
            type: LOGIN_FAIL
        });
    }
};

export const logout = () => dispatch => {
    dispatch({ type: LOGOUT });
};

This is my reducers/auth.js:

import {
    SIGNUP_SUCCESS,
    SIGNUP_FAIL,
    LOGIN_SUCCESS,
    LOGIN_FAIL,
    LOGOUT,
    AUTHENTICATED_FAIL,
    AUTHENTICATED_SUCCESS,
    USER_LOADED_SUCCESS,
    USER_LOADED_FAIL
} from '../actions/types';

const initialState = {
    access: localStorage.getItem('access'),
    refresh: localStorage.getItem('refresh'),
    isAuthenticated: null,
    user: null
};

export default function(state = initialState, action) {
    const { type, payload } = action;

    switch(type) {
        case AUTHENTICATED_SUCCESS:
            return {
                ...state,
                isAuthenticated: true
            }
        case LOGIN_SUCCESS:
            localStorage.setItem('access', payload.access);
            return {
                ...state,
                isAuthenticated: true,
                access: payload.access,
                refresh: payload.refresh
            }
        case USER_LOADED_SUCCESS:
            return {
                ...state,
                user: payload
            }
        case AUTHENTICATED_FAIL:
            return {
                ...state,
                isAuthenticated: false
            }
        case USER_LOADED_FAIL:
            return {
                ...state,
                user: null
            }
        case LOGIN_FAIL:
        case LOGOUT:
            localStorage.removeItem('access');
            localStorage.removeItem('refresh');
            return{
                ...state,
                access: null,
                refresh: null,
                isAuthenticated: false,
                user: null
            }
        default:
            return state
    }
}

If I log in and use the redux Devtool I can see this state:

{
  auth: {
    access: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjAzNDgzODY1LCJqdGkiOiJhYTAzYzIzNTUwN2M0YTkxYjA2NjNmNDc0ZTU2MjIxMSIsInVzZXJfaWQiOjF9.Jyld4U7i6EqmsNoi0_qT9O9Kcu1TiEuyLLYCWWaoBrU',
    refresh: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTYwMzU2OTk2NSwianRpIjoiOWIzMWIyN2M1ODkyNDRiZDk3Y2EwMDI1NTY2Mzk3ZWMiLCJ1c2VyX2lkIjoxfQ.UgH_753OoWD3NXiwPwa1645_vIHUl-FwyvQMJWMgHtk',
    isAuthenticated: true,
    user: {
      name: 'Jonas Levin',
      id: 1,
      email: '[email protected]'
    }
  }
}

But I can`t figure out how to display the data, for example user.name.
I already tried to use mapStateToProps in one of my components but I get the error: "TypeError: Cannot read property 'name' of undefined"

const mapStateToProps = state => ({
    userName: state.user.name,
    userEmail: state.user.email
});

Edit
This is the response data that I get. But as you can see there is another API call which is still from the login page where I was on before I got redirected to '/' and that light red /me call has an error message in it because when your on the login page you don`t have an access token. How can I access this response data in my Components to render the Name? Response data

Store.js:

import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

const initialState = {};

const middleware = [thunk];

const store = createStore(
    rootReducer,
    initialState,
    composeWithDevTools(applyMiddleware(...middleware))
);

export default store;

Upvotes: 1

Views: 238

Answers (3)

JonasLevin
JonasLevin

Reputation: 2089

I managed to access the username in my layout.js file by adding the state to the props:

const mapStateToProps = (state, ownProps) => {
  return {
    isAuthenticated: state.auth.isAuthenticated,
    user: state.auth.user,
    props: ownProps
  }
};

I used ownProps to be able to also use props.children in the layout container. Than I gave tham as parameters to the layout container and was able to access the username with user.name. I´m not entirely sure why it worked now and not before when I already tried to use mapStateToProps.

Upvotes: 1

ecoplaneteer
ecoplaneteer

Reputation: 1984

If you use a functional component, you could use useSelector hook.

const user = useSelector(state => state.auth.user)

Upvotes: 0

سعيد
سعيد

Reputation: 1764

this is how you should do it access auth reducer then th user

const mapStateToProps = state => ({
    userName: state.auth.user.name,
    userEmail: state.auth.user.email
});

this is how redux works , let's say this is your store


import {cartReducer} from './reducers/CartReducer'
import { authReducer } from './reducers/AuthReducer'
import { ordersReducer } from './reducers/OrdersReducer'
import { errorsReducer } from './reducers/ErrorsReducer'

const initialState={
    products:{
        items:[],
        filterdProducts:[]
    },
    cart:{
        items:[],
    },
    orders:{
        items:[],
        canOrder:true,
    },
   auth: {
    access: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjAzNDgzODY1LCJqdGkiOiJhYTAzYzIzNTUwN2M0YTkxYjA2NjNmNDc0ZTU2MjIxMSIsInVzZXJfaWQiOjF9.Jyld4U7i6EqmsNoi0_qT9O9Kcu1TiEuyLLYCWWaoBrU',
    refresh: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTYwMzU2OTk2NSwianRpIjoiOWIzMWIyN2M1ODkyNDRiZDk3Y2EwMDI1NTY2Mzk3ZWMiLCJ1c2VyX2lkIjoxfQ.UgH_753OoWD3NXiwPwa1645_vIHUl-FwyvQMJWMgHtk',
    isAuthenticated: true,
    user: {
      name: 'Jonas Levin',
      id: 1,
      email: '[email protected]'
    }
  },
    error:{
        msg:null,
        status:null,
        id:null
    }
}

const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__  || compose 

const  store = createStore(combineReducers({
        products: productsReducer,
        cart    : cartReducer ,
        orders  : ordersReducer ,
        auth    : authReducer,
        error   : errorsReducer ,
    }),
    initialState,
    composeEnhancer(applyMiddleware(thunk))
)
export default store

if you want to access user from any other component you'mm need to access auth reducer first , same for items you can either access products.items or cart .items and so on

Upvotes: 0

Related Questions