Reputation: 366
I have a simple 'post-like' application built with react and redux. When the user is signed in, he should see one navbar, when he is not, he should see another.
I am using redux-devtools to keep track of the state changes.
The issue is, that when I log in, I set a local storage token and when I log out, I set that token to null. This approach works fine, when I click on logout, I see the token being null
and user_authenticated
being false in the redux devtools, however when I go back to the homepage, or click the browser back button, it reverts to the login
state, I can then see the token value and is_authenticated
being set to true.
When I refresh the page, I get my desired behavior that is the user logs out, the token is set to null, and is_authenticated
set to false.
The weird thing is if I do console.log(store.getState())
, I get the updated/expected state there, but the devtools show the previous state, and the page even behaves like the previous state.
The same is the case with login, going back and forth in navigation gets me the old state that was there when I first visited that page.
State from my redux-devtools, after going back to the homepage, after logging out.
user: {
is_authenticated: true,
token: 'c80c632a6529b79d51fd3e2fc915cfb94834aa5237b4e65924c095ea58f289b3',
is_loading: false
},
State from store.getState()
on the same page, after moving back from logout
user:
is_authenticated: false
is_loading: false
token: "null"
Some of my components are listed below:
routes.js
import { Switch, Route } from 'react-router-dom';
import React, {Component} from 'react';
import Homepage from './components/homepage';
import Scammer from './components/scammer';
import ScammerCreate from './components/scammer_create'
import Navbar from './components/navbar'
import Register from './components/register'
import Login from './components/login'
import Logout from './components/logout'
import {store} from './index'
class Routes extends Component {
componentWillMount() {
store.dispatch({
type: 'get_user',
})
store.getState()
}
render(){
return(
<div>
<Navbar />
<Switch>
<Route path="/" exact component={Homepage}/>
<Route path="/scammer/create" exact component={ScammerCreate}></Route>
<Route path="/scammer/:id" exact component={Scammer}/>
<Route path="/auth/register" exact component={Register}></Route>
<Route path="/auth/login" exact component={Login}></Route>
<Route path="/auth/logout" exact component={Logout}></Route>
</Switch>
</div>
)
}
}
export default Routes;
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import promiseMiddleware from 'redux-promise';
import thunkMiddleware from 'redux-thunk'
import Routes from './routes'
import rootReducer from './reducers/index';
//const createStoreWithMiddleware = applyMiddleware(promiseMiddleware)(createStore)
const middleware = [promiseMiddleware,thunkMiddleware]
export const store = createStore(
rootReducer,
composeWithDevTools(applyMiddleware(...middleware))
)
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<Routes/>
</BrowserRouter>
</Provider>
, document.getElementById('root'));
user_reducer
let initialState = {
}
if (localStorage.getItem('token') != null && localStorage.getItem('token') != 'null'){
initialState['is_authenticated'] = true;
initialState['token'] = localStorage.getItem('token');
initialState['is_loading'] = false;
}
else{
initialState ={
'is_authenticated' : false,
'token' : localStorage.getItem('token'),
'is_loading' : false,
}
}
export default function user(state=initialState,action){
if (action.type == "registered") {
localStorage.setItem('token',action.payload.token);
return {...state, 'user_details': action.payload.user,'token':localStorage.getItem('token'),'is_authenticated':true,'is_loading':false}
}
else if ( action.type == "login" ){
console.log(action.payload)
localStorage.setItem('token',action.payload.token)
return {
...state,'is_authenticated' : true, 'token' : localStorage.getItem('token'),'is_loading' : false,
'user_details' : action.payload.user,
}
}
else if( action.type == "logout" ){
localStorage.setItem("token",null);
return {
...state,'is_authenticated' : false, 'token' : localStorage.getItem('token'), 'is_loading' : false
}
}
else if(action.type == 'get_user'){
const token = localStorage.getItem('token');
console.log(token)
if (token != null && token != 'null'){
return {
...state,'is_authenticated' : true, token : token, is_loading : false,
}
}
else {
return {
...state, is_authenticated : false, token: token, is_loading : false,
}
}
}
else{
return initialState;
}
}
Upvotes: 3
Views: 2260
Reputation: 202826
Redux reducers should always return a state value. In your case the issue is default returning the initial state instead of the current state.
Update the reducer to align with Redux reducer suggested patterns.
Example:
export default function user(state = initialState, action) {
switch (action.type) {
case "registered":
localStorage.setItem('token', action.payload.token);
return {
...state,
user_details: action.payload.user,
token: localStorage.getItem('token'),
is_authenticated: true,
is_loading: false
};
case "login":
localStorage.setItem('token',action.payload.token)
return {
...state,
is_authenticated: true,
token: localStorage.getItem('token'),
is_loading: false,
user_details: action.payload.user,
};
case "logout":
localStorage.setItem("token",null);
return {
...state,
is_authenticated: false,
token: localStorage.getItem('token'),
is_loading: false
};
case "get_user":
const token = localStorage.getItem('token');
return {
...state,
is_authenticated: token != null && token != 'null',
token,
is_loading: false,
};
default:
return state; // <-- no case matched, return current state
}
}
Upvotes: 0
Reputation: 366
Okay, so after days of debugging, and going through redux tutorials from start to end and still failing.
I finally realized that i was returning initialState for default action. So when ever a new component would load, it would get the initialState rather than updated state.
Upvotes: 2