React, Redux and Authentication - not finding state - reactjs

I am trying to intergrate redux-auth-wrapper into the react boilerplate.
According to my debugging tools, the browser has the state set correctly (user: {data: null, isLoading: false}), however the App is saying that state is undefined.
mapStateToProps does seem to be given the state correctly?
Reducer:
containers/App/reducer.js:
import { fromJS } from 'immutable';
import {
USER_LOGGING_IN,
USER_LOGGED_IN,
USER_LOGGED_OUT,
} from '../../constants';
const userInitialState = fromJS({
data: null,
isLoading: false,
});
function userReducer(state = userInitialState, action) {
switch (action.type) {
case USER_LOGGING_IN:
return state
.set('isLoading', true);
case USER_LOGGED_IN:
return state
.set('data', action.payload)
.set('isLoading', false);
case USER_LOGGED_OUT:
return userInitialState;
default:
return state;
}
}
export default userReducer;
containers/App/index.js
import React from 'react';
import { Route, NavLink } from 'react-router-dom';
import { connect } from 'react-redux';
import { logout } from '../../actions/user';
import Home from '../../components/Home';
const getUserName = (user) => {
if (user.data) {
return `Welcome ${user.data.name}`;
}
return ('Not logged in');
};
const UserName = ({ user }) => (<div>{getUserName(user)}</div>)
function App({ user, logout }) {
return (
<div>
<h1>Test App</h1>
<div>
<nav>
<NavLink exact to="/">Home</NavLink>
</nav>
<nav>
</nav>
<div>
<Route exact path="/" component={Home} />
</div>
</div>
</div>
);
}
const mapStateToProps = (state) => ({
user: state.user, -- this is undefined when it should not be
});
export default connect(mapStateToProps, { logout })(App);
reducers.js:
/**
* Combine all reducers in this file and export the combined reducers.
*/
import { fromJS } from 'immutable';
import { combineReducers } from 'redux-immutable';
import { LOCATION_CHANGE } from 'react-router-redux';
import languageProviderReducer from 'containers/LanguageProvider/reducer';
import userReducer from 'containers/App/reducer';
/*
* routeReducer
*
* The reducer merges route location changes into our immutable state.
* The change is necessitated by moving to react-router-redux#5
*
*/
// Initial routing state
const routeInitialState = fromJS({
location: null,
});
/**
* Merge route into the global application state
*/
function routeReducer(state = routeInitialState, action) {
switch (action.type) {
/* istanbul ignore next */
case LOCATION_CHANGE:
return state.merge({
location: action.payload,
});
default:
return state;
}
}
/**
* Creates the main reducer with the dynamically injected ones
*/
export default function createReducer(injectedReducers) {
return combineReducers({
route: routeReducer,
language: languageProviderReducer,
user: userReducer,
...injectedReducers,
});
}
This is just the react boilerplate, but mixed with the react router 4 example (auth.js, constants.js, App/reducer.js, App/index.js etc)

State is an immutable object. For you to use it as an simple javacsript object, use .toJS().
const mapStateToProps = (immutableState) => {
const state = immutableState.toJS();
return {
user: state.user,
--this should work correctly now
};
};
Let me know if this solves.

const userInitialState = fromJS({
data: null,
isLoading: false,
});
would this not return state.data and state.isLoading?
const mapStateToProps = (state) => ({
user: state.user, -- this is undefined when it should not be
});
I don't see anywhere that you've declared 'user' as a key on your state object.

Related

the reducer is not performed

I just tried make simply reducer in react redux but it never called. After a lot trial i have no idea why it's not working. console.log in action is showing but reducer never is called.
import React from "react";
import { connect } from "react-redux";
import * as actions from "store/actions";
function Login(props) {
const login = (e) => {
e.preventDefault();
props.login();
};
return (
<form onSubmit={login}>
<button> login </button>
</form>
);
}
const mapDispatchToProps = (dispatch) => {
return {
login: () => dispatch(actions.login),
};
};
export default connect(null, mapDispatchToProps)(Login);
actions file- i'm here console.log is showing correctly
import * as actionsTypes from "./actionTypes";
export const logout = () => {
return {
type: actionsTypes.AUTH_LOGOUT,
};
};
export const login = () => {
console.log("i'm here")
return {
type: actionsTypes.AUTH_LOGIN,
};
};
reducer
import * as actionTypes from "../actions/actionTypes";
const initialState = {
isLogged: false,
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.AUTH_LOGIN:
return {
...state,
isLogged: true,
};
case actionTypes.AUTH_LOGOUT:
return {
...state,
isLogged: false,
};
default:
return state;
}
};
export default reducer;
many thanks for help
Probably, you forget to make a configuration of the store itself? :)
Something like that:
// at configureStore.js
import { createStore } from 'redux';
import reducer from '../path/to/your/root/reducer'; // assuming that you use combineReducer function to gather all reducers in one place
export default createStore(reducer);
Then in your app root you need to wrap entry component with the store provider:
import store from './store/configureStore';
import { Provider } from 'react-redux';
export default () => (
<Provider store={store}>
<AppRootComponent />
</Provider>
);
AppRootComponent -> your initial app component
For reference - how to configure store
UPD:
Looks like you were trying to pass the action creator to the dispatch function, instead of invoking it actually. Just make a call of that creator in the dispatch:
login: () => dispatch(actions.login()),
BTW, here is the working example of your case

React component not updating on final dispatch and update of redux state

I am fairly new to React and Redux and I have an issue with my component not updating on the final dispatch that updates a redux store. I am using a thunk to preload some data to drive various pieces of my site. I can see the thunk working and the state updating seemingly correctly but when the data fetch success dispatch happens, the component is not seeing a change in state and subsequently not re rendering. the interesting part is that the first dispatch which sets a loading flag is being seen by the component and it is reacting correctly. Here is my code:
actions
import { programsConstants } from '../constants';
import axios from 'axios'
export const programsActions = {
begin,
success,
error,
};
export const loadPrograms = () => dispatch => {
dispatch(programsActions.begin());
axios
.get('/programs/data')
.then((res) => {
dispatch(programsActions.success(res.data.results));
})
.catch((err) => {
dispatch(programsActions.error(err.message));
});
};
function begin() {
return {type:programsConstants.BEGIN};
}
function success(data) {
return {type:programsConstants.SUCCESS, payload: data};
}
function error(message) {
return {type:programsConstants.ERROR, payload:message};
}
reducers
import {programsConstants} from '../constants';
import React from "react";
const initialState = {
data: [],
loading: false,
error: null
};
export function programs(state = initialState, action) {
switch (action.type) {
case programsConstants.BEGIN:
return fetchPrograms(state);
case programsConstants.SUCCESS:
return populatePrograms(state, action);
case programsConstants.ERROR:
return fetchError(state, action);
case programsConstants.EXPANDED:
return programsExpanded(state, action);
default:
return state
}
}
function fetchPrograms(state = {}) {
return { ...state, data: [], loading: true, error: null };
}
function populatePrograms(state = {}, action) {
return { ...state, data: action.payload, loading: false, error: null };
}
function fetchError(state = {}, action) {
return { ...state, data: [], loading: false, error: action.payload };
}
component
import React from "react";
import { connect } from 'react-redux';
import { Route, Switch, Redirect } from "react-router-dom";
import { Header, Footer, Sidebar } from "../../components";
import dashboardRoutes from "../../routes/dashboard.jsx";
import Loading from "../../components/Loading/Loading";
import {loadPrograms} from "../../actions/programs.actions";
class Dashboard extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.props.dispatch(loadPrograms());
}
render() {
const { error, loading } = this.props;
if (loading) {
return <div><Loading loading={true} /></div>
}
if (error) {
return <div style={{ color: 'red' }}>ERROR: {error}</div>
}
return (
<div className="wrapper">
<Sidebar {...this.props} routes={dashboardRoutes} />
<div className="main-panel" ref="mainPanel">
<Header {...this.props} />
<Switch>
{dashboardRoutes.map((prop, key) => {
let Component = prop.component;
return (
<Route path={prop.path} component={props => <Component {...props} />} key={key} />
);
})}
</Switch>
<Footer fluid />
</div>
</div>
);
}
}
const mapStateToProps = state => ({
loading: state.programs.loading,
error: state.programs.error
});
export default connect(mapStateToProps)(Dashboard);
The component should receive updated props from the success dispatch and re render with the updated data. Currently the component only re renders on the begin dispatch and shows the loading component correctly but doesn't re render with the data is retrieved and updated to the state by the thunk.
I've researched this for a couple days and the generally accepted cause for the component not getting a state refresh is inadvertent state mutation rather than returning a new state. I don't think I'm mutating the state but perhaps I am.
Any help would much appreciated!
Update 1
As requested here's the code for creating the store and combining the reducers
store:
const loggerMiddleware = createLogger();
const composeEnhancers =
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(
thunk,
loggerMiddleware)
);
export const store = createStore(rootReducer, enhancer);
reducer combine:
import { combineReducers } from 'redux';
import { alert } from './alert.reducer';
import { programs } from './programs.reducer';
import { sidenav } from './sidenav.reducer';
const rootReducer = combineReducers({
programs,
sidenav,
alert
});
export default rootReducer;
The 2nd param is expected to be [preloadedState]:
export const store = createStore(rootReducer, {} , enhancer);
axios.get return a promise that you need to await for to get your data:
Try this:
export const loadPrograms = () => async (dispatch) => {
dispatch(programsActions.begin());
try {
const res = await axios.get('/programs/data');
const data = await res.data;
console.log('data recieved', data)
dispatch(programsActions.success(data.results));
} catch (error) {
dispatch(programsActions.error(error));
}
};
const mapStateToProps = state => ({
loading: state.programs.loading,
error: state.programs.error,
data: state.programs.data,
});
Action Call
import React from 'react';
import { connect } from 'react-redux';
import { loadPrograms } from '../../actions/programs.actions';
class Dashboard extends React.Component {
componentDidMount() {
// Try to call you action this way:
this.props.loadProgramsAction(); // <== Look at this
}
}
const mapStateToProps = state => ({
loading: state.programs.loading,
error: state.programs.error,
});
export default connect(
mapStateToProps,
{
loadProgramsAction: loadPrograms,
},
)(Dashboard);
After three days of research and refactoring, I finally figured out the problem and got it working. Turns out that the version of react-redux is was using (6.0.1) was the issue. Rolled back to 5.1.1 and everything worked flawlessly. Not sure if something is broken in 6.0.1 or if I was just using wrong.

User data coming back undefined with Redux React App

Currently I am trying to pass user data through my react app with Redux. I have created a user API with a django backend that is definately working, as I am able to go the url and see all the json that comes out of it. However, when I try to pass it into a component I keep getting undefined. Here is my code:
userActions.js:
import Axios from "axios";
export function getUser() {
const id = this.params.match.id
return dispatch => {
dispatch(fetchUserBegin());
return Axios.get(`/api/user/${id}`)
.then((res) => {
this.setState({
user: res.data,
})
})
}
}
export const FETCH_USER_BEGIN = 'FETCH_USER_BEGIN';
export const FETCH_USER_SUCCESS = 'FETCH_USER_SUCCESS';
export const FETCH_USER_FAILURE = 'FETCH_USER_FAILURE';
export const fetchUserBegin = () => ({
type: FETCH_USER_BEGIN
});
export const fetchUserSuccess = user => ({
type: FETCH_USER_SUCCESS,
payload: { user }
});
export const fetchUserFailure = error => ({
type: FETCH_USER_FAILURE,
payload: { error }
});
userReducer.js
import { FETCH_USER_BEGIN, FETCH_USER_SUCCESS, FETCH_USER_FAILURE } from '../actions/actionTypes'
const initialState = {
user: {},
loading: false,
error: null
};
export default function productReducer(state = initialState, action) {
switch(action.type) {
case FETCH_USER_BEGIN:
// Mark the state as "loading" so we can show a spinner or something
// Also, reset any errors. We're starting fresh.
return {
...state,
loading: true,
error: null
};
case FETCH_USER_SUCCESS:
// All done: set loading "false".
// Also, replace the items with the ones from the server
return {
...state,
loading: false,
user: action.user
};
case FETCH_USER_FAILURE:
// The request failed, but it did stop, so set loading to "false".
// Save the error, and we can display it somewhere
// Since it failed, we don't have items to display anymore, so set it empty.
// This is up to you and your app though: maybe you want to keep the items
// around! Do whatever seems right.
return {
...state,
loading: false,
error: action.payload.error,
user: {}
};
default:
// ALWAYS have a default case in a reducer
return state;
}
}
And the display component:
UserInformation.js:
import React from "react";
import { connect } from "react-redux";
import { getUser } from "../store/actions/userActions";
class UserDetailView extends React.Component {
componentDidMount() {
this.props.dispatch(getUser());
}
render() {
const { user } = this.props;
console.log(user)
return (
<ul>
{user.map(user =>
<li key={user.id}>{user.username}</li>
)}
</ul>
);
}
}
const mapStateToProps = state => ({
user: state.user,
});
export default connect(mapStateToProps)(UserDetailView);
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { createStore, compose, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import reducer from './store/reducers/auth';
const composeEnhances = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore(reducer, composeEnhances(
applyMiddleware(thunk)
))
const app = (
<Provider store={store}>
<App />
</Provider>
)
ReactDOM.render(app, document.getElementById('root'));
registerServiceWorker();
Anyone got any ideas why this isn't working?
You're not supposed to setState() in that action creator:
this.setState({
user: res.data,
})
you should dispatch an action instead
Try this:
export function getUser() {
const id = this.params.match.id
return dispatch => {
dispatch(fetchUserBegin());
return Axios.get(`/api/user/${id}`)
.then( res => {
dispatch(fetchUserSuccess(res.data);
})
}
}
You should pass the mapDispatchToProps function to the connect() method as the second argument, like this:
import React from "react";
import { connect } from "react-redux";
import { getUser } from "../store/actions/userActions";
class UserDetailView extends React.Component {
componentDidMount() {
this.props.getUser() //fixed
}
render() {
const { user } = this.props;
console.log(user)
return (
<ul>
{user.map(user =>
<li key={user.id}>{user.username}</li>
)}
</ul>
);
}
}
const mapStateToProps = state => ({
user: state.user,
});
const mapDispatchToProps = dispatch => ({ //added
getUser: dispatch(getUser())
})
export default connect(mapStateToProps,mapDispatchToProps)(UserDetailView); //fixed
And also fix this:
case FETCH_USER_SUCCESS:
// All done: set loading "false".
// Also, replace the items with the ones from the server
return {
...state,
loading: false,
user: action.payload.user //fixed
};

Components not having the expected props, while React and Redux Dev Tools are having the expected States and Props

I am learning react-redux, so I decided to implement what I have been learning. But I am have a bug challenge. So I console.logged this.props.users from mapStateToProps function.
I believe there's something I not doing right which I don't understand. Please an explanation in other to move on. Thanks you so much for helping out.
Here is my code.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchUsers } from '../actions/userAction';
import UserList from '../components/UserList';
class UserPage extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.fetchUsers();
}
componentDidMount() {
console.log(this.props.users);
}
render() {
return (
<div>
<h2>Users Page</h2>
<UserList users={this.props.users} />
</div>
);
}
}
const mapStateToProps = state => {
return {
users: state.userReducer.users
};
};
const mapDispatchToProps = dispatch => {
return {
fetchUsers: () => dispatch(fetchUsers())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(UserPage);
So this is what I get from the chrome console - Empty arrays.
props showing empty arrays
But when I check the React DevTool and Redux DevTool, they display the expected Props and States respectively. Below are the snapshot of the dev tools
React devtool shows the correct Props
Redux devtool show the correct States and Actions
userAction.js
import axios from 'axios';
import * as types from './actionTypes';
export let fetchingUser = () => {
return {
type: types.FETCHING_USERS
};
};
export let fetchedUser = payload => {
return {
type: types.FETCHED_USER,
payload
};
};
export let fetchUser_error = () => {
return {
type: types.FETCH_USER_ERROR
};
};
export let fetchUsers = () => {
let url = 'https://eventcity.herokuapp.com/api/v1/users';
return dispatch => {
dispatch(fetchingUser());
return axios
.get(url)
.then(response => {
const users = response.data.data;
dispatch(fetchedUser(users));
})
.catch(err => {
dispatch(fetchUser_error());
});
};
};
userReducer.js
import * as types from '../actions/actionTypes';
import initialState from './initialState';
const userReducer = (state = initialState, action = {}) => {
switch (action.type) {
case types.FETCHING_USERS:
return { ...state, users: [], error: null, loading: true };
case types.FETCHED_USER:
return { ...state, users: action.payload, error: null, loading: false };
case types.FETCH_USER_ERROR:
return {
...state,
users: [],
error: { message: 'Error loading data from the API' },
loading: false
};
default:
return state;
}
};
export default userReducer;
configureStore.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import rootReducer from '../reducer/rootReducer';
const configureStore = () => {
return createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)));
};
export default configureStore;
rootReducer.js
import { combineReducers } from 'redux';
import userReducer from './userReducer';
const rootReducer = combineReducers({
userReducer
});
export default rootReducer;
I think you might want to check this
https://github.com/reactjs/react-redux/issues/129. Your problem is using componentDidMount and componentWillMount without having a better understanding of what they are used for.
The problem is not with redux, all you need to understand is that your fetchUsers request is async and componentDidMount function is only executed once after the component has rendered and it may so happen that the data is not present by the time componentDidMount function is executed and hence your console.log(this.props.users); return empty array, Log it in the render method and you will see the correct data
class UserPage extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.fetchUsers();
}
render() {
console.log(this.props.users);
return (
<div>
<h2>Users Page</h2>
<UserList users={this.props.users} />
</div>
);
}
}

Passing props via redux (or a simpler way if possible) through Route or Link

I have been flipping through search results and googling like an addict chasing that amber baby rattle. I am finding nothing which uses React-Router -v 4. I am building a simple(lol simple in theory) application which uses axios to pass user profiles to a Home component which then maps the response and displays a gird of avatars and names. This works fine. The problem is, I want to click on one of those avatars then pass that specific data which is associated with that avatar to a new individual user page by using the id params. So click on avatar open user profile. I know before react-router -v 4 it seems you could actually just pass a method directly into a route but this is not working for me. Any help would greatly help me with this puzzle.
Action
import axios from 'axios'
const URI = '###'
export function fetchUsers () {
return function (dispatch) {
axios.get (URI)
.then ((response) => {
dispatch({
type: 'FETCH_USERS_FULFILLED',
payload: response.data.users
})
})
.catch ((err) => {
dispatch({
type: 'FETCH_USERS_REJECTED',
payload: err
})
})
}
}
Reducer
const initialState = {
users: [],
fetching: false,
fetched: false,
error: null
}
export default function reducer (state = initialState, action) {
switch (action.type) {
case 'FETCH_USERS_LOADING': {
return {
...state,
fetching: true,
fetched: false,
users: action.payload
}
}
case 'FETCH_USERS_FULFILLED': {
return {
...state,
fetching: false,
fetched: true,
users: action.payload
}
}
case 'FETCH_USERS_REJECTED': {
return {
...state,
fetching: false,
error: action.payload
}
}
}
return state
}
Store
import { applyMiddleware, createStore, compose } from 'redux'
import { createLogger } from 'redux-logger'
import thunk from 'redux-thunk'
import reducers from '../reducers'
const middleware = applyMiddleware(thunk, createLogger())
const store = createStore(
reducers,
compose(middleware, window.devToolsExtension ?
window.devToolsExtension() : f => f)
)
export default store
Home
import React, { Component } from 'react'
import { connect } from 'react-redux'
import Profile from '../Profile'
import { fetchUsers } from '../redux/actions/userActions'
class Home extends Component {
constructor(props) {
super(props);
this.state = {
}
}
componentDidMount() {
this.props.fetchUsers();
}
render () {
return (
<div className={'view-wrapper'}>
<div className={'home-container'}>
{this.props.users.map((user, i) =>
<Profile {...this.props} key={i} i={i} user={user}/>
)}
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
users: state.user.users,
fetched: state.user.fetched,
error: state.user.error
}
};
const mapDispatchToProps = (dispatch) => {
return {
fetchUsers: () => dispatch(fetchUsers())
}
};
export default connect(mapStateToProps, mapDispatchToProps)(Home);
Profile
import React from 'react'
import { Link } from 'react-router-dom'
const Profile = (props) => (
<div className='profile-container'>
<Link to={`/profile/${props.id}`}>
<img
src={props.avatar}
className={'user-avatar'}
alt={props.name}
/>
</Link>
</div>
)
export default Profile
Routes
<Switch>
<Route exact path='/' component={Home} />
<Route path='/profile/:id' component={Profile}/>
</Switch>
You're half way there. You don't need to pass the data (props) along to the individual user page, instead, use the id in the route so the individual user page knows what record in the Store it wants. In your individual user component:
const mapStateToProps = (state, props) => {
return {
user: state.user.users.find( (user) => (user.id === props.match.params.id) )
};
};
Then you can access 'user' as a prop in the component.

Resources