Jen Born
Jen Born

Reputation: 741

I can't get a value out of Redux store in App component

I'm using Redux DevTools Chrome Extension and can see in it that auth.isSignedIn in the Redux store is populated properly through the Register component. But then when it rerenders when I redirect to home, I can't access this.state.auth.isSignedIn in App.js to properly route the user to the normal home page component. What am I doing wrong here? This is my first independent React project after doing training (but I've been a developer for a long time).

enter image description here

App.js App Component

import React  from 'react';
import { Router, Route, Switch } from 'react-router-dom';
import { connect } from 'react-redux';
import Header from './Header';
import history from '../history';
import Login from './Login/Login';
import Register from './Login/Register';

class App extends React.Component {
    state = { isSignedIn: null };
    
    render(){
      
        console.log('getting isSigned in');
        if(this.state.auth && this.state.auth.isSignedIn){
            console.log('it has VALUE');
            console.log(this.state.auth.isSignedIn);
        }else{
            console.log('no isSignedIn');
        }
    }
}
const mapStateToProps = (state) => {
    return { isSignedIn: state.auth.isSignedIn };
};
export default connect(mapStateToProps)(App);

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose } from 'redux';
import reduxThunk from 'redux-thunk';

import App from './components/App';
import reducers from './reducers';

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
    reducers,
    composeEnhancers(applyMiddleware(reduxThunk))
    );

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>, 
document.querySelector('#root')
);

Register.js Register Component

import React from 'react';
import { register } from '../../actions';
import { connect } from 'react-redux';
import LoginForm from './LoginForm';
    
class Register extends React.Component {
            
    onSubmit = (formValues) => {
        this.props.register(formValues);
    }

    render(){
        return(
            <div >
             <LoginForm formHeading="Register Now" onSubmit={this.onSubmit} />
            </div>
          );
    }
}

const mapStateToProps = (state) => {
    return { isSignedIn: state.auth.isSignedIn };
};
export default connect(mapStateToProps, { register })(Register);

actions/index.js Action Creator

import listItems from '../apis/listItems';
import user from '../apis/user';
import history from '../history';
import { SIGN_IN, SIGN_OUT, REGISTER, CREATE_LIST_ITEM, EDIT_LIST_ITEM, DELETE_LIST_ITEM, FETCH_LIST_ITEM, FETCH_LIST_ITEMS, MARK_COMPLETED_LIST_ITEM } from './types';

export const signIn = formValues => async (dispatch, getState) => {
    let data = new FormData();
    data.append('request', 'login');
    data.append('email', formValues.email);
    data.append('password', formValues.password);

    const response = await listItems.post('/user', data);

    dispatch({ type: SIGN_IN, payload: response.data });
    history.push('/');
    
    return {
        type: SIGN_IN,
        payload: response.data
    };
};

export const register = (formValues) => async (dispatch, getState) => {
    let data = new FormData();
    data.append('request', 'register');
    data.append('email', formValues.email);
    data.append('password', formValues.password);

    const response = await user.post('/user', data);

    dispatch({ type: REGISTER, payload: response.data });
    history.push('/');
    
    return {
        type: REGISTER,
        payload: response.data
    };
};


export const signOut = () => {
    return {
        type: SIGN_OUT
    };
};

export const createListItem = formValues => async (dispatch, getState) => {
    let data = new FormData();
    data.append('listItem', formValues.list_item);
    data.append('completionLabel', formValues.completion_label);

    const response = await listItems.post('/listItemsMaintenance', data);

    dispatch({ type: CREATE_LIST_ITEM, payload: response.data });
    history.push('/');
};
export const fetchListItems = () => async dispatch => {
    const response = await listItems.get('/listItemsMaintenance');

    dispatch({ type: FETCH_LIST_ITEMS, payload: response.data });
};

export const fetchListItem = (id) => async dispatch => {
    let data = new FormData();
    data.append('list_item_id', id);
    const response = await listItems.get(`/listItemsMaintenance?list_item_id=${id}`);
        
    dispatch({ type: FETCH_LIST_ITEM, payload: response.data});
};

export const editListItem = (list_item_id, formValues) => async dispatch => {
    let data = new FormData();
    data.append('request', 'edit');
    data.append('list_item_id', list_item_id);
    data.append('listItem', formValues.list_item);
    data.append('completionLabel', formValues.completion_label);

    const response = await listItems.post(`/listItemsMaintenance`, data);

    dispatch({ type: EDIT_LIST_ITEM, payload: {...formValues, list_item_id } });
    history.push('/');
};

export const markListItemCompleted = (list_item_id) => async dispatch => {
    let data = new FormData();
    data.append('request', 'markCompleted');
    data.append('list_item_id', list_item_id);

    const response = await listItems.post('/listItemsMaintenance', data);
    // not sure what the payload actually needs to be
    dispatch({ type: MARK_COMPLETED_LIST_ITEM, payload: {...response.data, list_item_id } });
    history.push('/');
};

export const deleteListItem = (list_item_id) => async dispatch => {
    let data = new FormData();
    data.append('request', 'delete');
    data.append('list_item_id', list_item_id);

    await listItems.post(`/listItemsMaintenance`, data);

    dispatch({ type: DELETE_LIST_ITEM, payload: list_item_id });
    history.push('/');

};

reducers/authReducer.js Reducer

import { SIGN_IN, SIGN_OUT, REGISTER } from '../actions/types';

const INITIAL_STATE = {
    isSignedIn: null,
    userId: null
};

export default (state = INITIAL_STATE, action) => {
    switch (action.type){
        case REGISTER:
            return {...state, isSignedIn: true, userId: action.payload };
        case SIGN_IN:
            return {...state, isSignedIn: true, userId: action.payload };
        case SIGN_OUT:
            return {...state, isSignedIn: false, userId: null};
        default:
            return state;
    }
};

Upvotes: 0

Views: 612

Answers (3)

Hairy Cat
Hairy Cat

Reputation: 51

You're mapping your redux state to your props with this function:

const mapStateToProps = (state) => {
    // Suggestion, map the whole auth object:
    // return { auth: state.auth };
    return { isSignedIn: state.auth.isSignedIn };
};

Here you say you want the property isSignedIn to be populated with the redux state.auth.isSignedIn.
As the name of the function describes: you're mapping your redux state to your PROPS.

In your code you used the state again to try to get the previously mapped value.

class App extends React.Component {
    // state = { isSignedIn: null };  This could be removed
    // For simplicity and readability's sake I suggest to
    // destructure your props into seperate variables like this
    const { isSignedIn } = this.props; // <--- Make sure to use PROPS and not STATE

    // FYI you mapped your redux state to an object with property isSignedIn,
    // and only mapped that variable, as stated before
    // You did not map the whole auth object so you can't reach that here

    render(){
      
        console.log('getting isSigned in');
        // if(this.state.auth && this.state.auth.isSignedIn){ <--- Your code
        // I liked your comparison, above this line, but you didn't map the
        // whole object so you can't check if auth is null here
        if(isSignedIn){
            console.log('it has VALUE');
            console.log(isSignedIn);
        }else{
            console.log('no isSignedIn');
        }
    }
}

Upvotes: 1

Shmili Breuer
Shmili Breuer

Reputation: 4147

You redux state gets added to this.props when you pass in the mapStateToProps argument to the connect function.

In your case you should be able to access isSignedIn by using this.props.isSignedIn

Upvotes: 0

Kishieel
Kishieel

Reputation: 2053

To get something from store you should use hook names useSelector from react-redux

import { useSelector } from 'react-redux'

// inside component
const state = useSelector( state => state ) // return whole store
const isSignedIn = useSelector( state => state.auth.isSignedIn ) // return only isSignedIn value

Upvotes: 0

Related Questions