2

Beginner question. I want to pass a user object to a component from store as a prop, but the component doesn't get it (undefined). I get the user object from a third party service authentication service (google firebase)

The middleware actually logs out in the console that the action of type SET_CURRENT_USER takes place, and next state indeed will have a user.currentUser set to the object returned from the login service (NOT UNDEFINED).

However, the component doesn't re-render and doesn't seem to receive the object as prop

The component, in which the prop is undefined

import React from 'react';
import { connect } from 'react-redux';
import { auth } from "../../firebase/firebase.utils";

export const Navbar = ({ currentUser }) => {
    return (
        /* A LOT OF JSX CODE. currentUser IS UNDEFINED */
    );
};

const mapStateToProps = state => ({
    currentUser: state.user.currentUser
});

export default connect(mapStateToProps)(Navbar);

The App component, which has the above component as a child. Also, I'm trying to set the store to contain the user object in the componentDidMount()

import React from 'react';
import Homepage from "./pages/homepage";
import { Route, Switch } from 'react-router-dom';
import { connect } from 'react-redux';

import Login from "./pages/login";
import Register from "./pages/register";
import { Navbar } from "./components/navbar/navbar";

import { auth } from "./firebase/firebase.utils";
import { setCurrentUser } from "./redux/user/user.actions";

class App extends React.Component {
    unsubscribeFromAuth = null;

    componentDidMount() {
        this.unsubscribeFromAuth = auth.onAuthStateChanged(async userAuth => {
            if(userAuth) {
                (async () => {
                    const rawResponse = await fetch(/* JUST AN ASYNC FUNCTION TO POST TO BACKEND*/);
                })();
            }
            this.props.setCurrentUser(userAuth); /*HERE IM TRYING TO SET THE STORE*/
        })
    }

    componentWillUnmount() {
        this.unsubscribeFromAuth();
    }

    render() {
        return (
            <div>
                <Navbar /> /* THE COMPONENT WHICH SHOULD GET THE USER OBJECT AS PROP */
                <Switch>
                    <Route exact={true} path={'/register'} component={Register} />
                    <Route exact={true} path={'/login'} component={Login} />
                    <Route path={'/'} component={Homepage} />
                </Switch>
            </div>
        );
    }
}

const mapDispatchToProps = (dispatch) => ({
    setCurrentUser: user => dispatch(setCurrentUser(user))
});

export default connect(null, mapDispatchToProps)(App);

The index component

import React from 'react';
import ReactDOM from 'react-dom';
import App from "./App";
import { BrowserRouter } from "react-router-dom";
import { Provider } from 'react-redux';

import store from "./redux/store";

ReactDOM.render(
    <Provider store={store} > /* HERE IS STORE PROVIDED FROM IMPORT*/
        <BrowserRouter>
            <App/>
        </BrowserRouter>
    </Provider>,
    document.getElementById('root')
);

Root reducer

import { combineReducers } from "redux";

export default combineReducers({
    user: userReducer
});

User reducer

const INITIAL_STATE = {
    currentUser: null
};

const userReducer = (state = INITIAL_STATE, action) => {
    switch (action.type) {
        case 'SET_CURRENT_USER':
            return {
                ...state,
                currentUser: action.payload
            };
        default:
            return state;
    }
};

export default userReducer;

User action

export const setCurrentUser = user => ({
    type: 'SET_CURRENT_USER',
    payload: user
});

The store

import { createStore, applyMiddleware } from "redux";
import logger from 'redux-logger';

import rootReducer from './root-reducer';

const middlewares = [logger];

const store = createStore(rootReducer, applyMiddleware(...middlewares));

export default store;

1 Answer 1

3

You're doing both a named and default export for Navbar. The default export gets wrapped by the connect HOC that adds currentUser to its props. The named export does not.

You import it named like this: import { Navbar } from. Instead use the default export: import Navbar from.

Then I would suggest removing the named export to avoid future confusion.

Sign up to request clarification or add additional context in comments.

4 Comments

Having the named export is valuable for testing, though (and allows disconnected use, which can have value). It shouldn't be called the same thing, obviously :)
@DaveNewton yeah, that's true. I think if you want to support both connected and disconnected the imports should be swapped for clarity. Make the component itself the default export, and then name the connected export something like ConnectedNavbar.
@BrianThompson Depends on primary usecase, really. This conundrum is one unfortunate side effect of people thinking the connection should happen in the component file itself (the producer) instead of where the component is actually used (the consumer). In general I advocate only exporting the unconnected component, and doing the connects elsewhere.
Actually I agree 100%. I prefer to keep components like this one completely ignorant of redux, but that's ultimately more of a preference. In my comment, I was only suggesting that if you wanted to keep both exports, it might be clearer to name the one that does something extra.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.