'React-Redux state lost after refresh
I'm really new to React and Redux, I've been following Stephen Grider's Advanced React and Redux course and I'm doing the client side of the authentication. I already have a token saved on my local storage and everything seemed to work fine until I refreshed the page. When I sign in/sign up the navigation changes to display the log out button but then if I manually refresh the page the navigation changes back to display the sign in/sign up buttons.
I'm really new to this and have no idea what should I include as code snippets. I'll leave the reducer and the actions/index.js. Also this is a lik for my git repository.
actions/index.js
import axios from 'axios';
import { browserHistory } from 'react-router';
import { push } from 'react-router-redux';
import { AUTH_USER, UNAUTH_USER, AUTH_ERROR } from './types';
const API_URL = 'http://localhost:3000';
export function signinUser({ username, password }) {
return function(dispatch) {
// Submit username/password to the server
axios
.post(`${API_URL}/signin`, { username, password })
.then(response => {
// If request is good...
// - Update state o indicate user is authenticated
dispatch({ type: AUTH_USER });
// - Save the JWT token to local storage
localStorage.setItem('token', response.data.token);
// - Redirect to the route '/feature'
browserHistory.push('/feature');
})
.catch(() => {
// If request is bad...
// -Show an error to the user
dispatch(authError('Bad login info'));
});
};
}
export function signupUser({ username, email, password }) {
return function(dispatch) {
axios
.post(`${API_URL}/signup`, { username, email, password })
.then(response => {
dispatch({ type: AUTH_USER });
localStorage.setItem('token', response.data.token);
browserHistory.push('/feature');
})
.catch(response => {
// TODO
console.log(response);
dispatch(authError('There was an error'));
});
};
}
export function authError(error) {
return {
type: AUTH_ERROR,
payload: error
};
}
export function signoutUser() {
localStorage.removeItem('token');
return { type: UNAUTH_USER };
}
reducer/auth_reducer.js
import { AUTH_USER, UNAUTH_USER, AUTH_ERROR } from '../actions/types';
export default function(state = {}, action) {
switch (action.type) {
case AUTH_USER:
return { ...state, error: '', authenticated: true };
case UNAUTH_USER:
return { ...state, authenticated: false };
case AUTH_ERROR:
return { ...state, error: action.payload };
}
return state;
}
Thanks in advance, if you need any extra code snippet just please let me know.
Solution 1:[1]
do not reinvent the wheel
To store redux state even after page refresh you can use
https://www.npmjs.com/package/redux-persist
It is easy to implement and robust.
Solution 2:[2]
In your reducer file reducer/auth_reducer.js you can define the initial state of the reducer.
const initialState = {
user: localStorage.getItem('user'), foo:'bar',
};
export default function(state = initialState, action) {
...
in your initialState you can load stuff from localstorage or from a cookie (cookie is preferred for auth stuff).
initialState can also be setup in your createStore. It's up to you. Where you need the initial state. I use async for my routes so I can't use createStore to hold all my initial state since some routes maybe never be loaded.
const initialState = {
user: localStorage.getItem('user'),
};
const store = createStore(mainReducer, initialState);
There is a library you can use called redux-persist. This will give you more control of what state you want to keep. (https://github.com/rt2zz/redux-persist)
Solution 3:[3]
To retain Redux state through page refreshes, you need to persist the app state by storing it in localStorage and retrieve it on page load. Try to dispatch an action in the componentDidMount of your App component, which retrieves the data from the localStorage
Solution 4:[4]
You need to persist app state in localStorage. Here is a tutorial made by Dan Abramov, creator of redux.
Solution 5:[5]
Do something like that: i used this method for my project
function saveToLocalStorage(store) {
try {
const serializedStore = JSON.stringify(store);
window.localStorage.setItem('store', serializedStore);
} catch(e) {
console.log(e);
}
}
function loadFromLocalStorage() {
try {
const serializedStore = window.localStorage.getItem('store');
if(serializedStore === null) return undefined;
return JSON.parse(serializedStore);
} catch(e) {
console.log(e);
return undefined;
}
}
const persistedState = loadFromLocalStorage();
const store = createStore(reducer, persistedState);
store.subscribe(() => saveToLocalStorage(store.getState()));
Solution 6:[6]
we can setup store to listen the sessionStore or localStorage values, so that value will get preserve,
for example
import { createStore, applyMiddleware, compose } from 'redux';
import { routerMiddleware } from 'react-router-redux';
import thunk from 'redux-thunk';
import { createBrowserHistory as createHistory } from 'history';
// import createHistory from 'history/createBrowserHistory';
import rootReducer from '@reducers';
import ApiClient from '@helpers/ApiClient';
import createMiddleware from '@reducers/middleware/clientMiddleware';
export const history = createHistory();
const client = new ApiClient();
const initialState = { users: JSON.parse(window.sessionStorage.getItem('redux') || '{}') };
const enhancers = [];
const middleware = [
createMiddleware(client),
thunk,
routerMiddleware(history)
];
if (process.env.NODE_ENV === 'development') {
const devToolsExtension = window.devToolsExtension;
if (typeof devToolsExtension === 'function') {
enhancers.push(devToolsExtension());
}
}
const composedEnhancers = compose(
applyMiddleware(...middleware),
...enhancers
);
const store = createStore(
rootReducer,
initialState,
composedEnhancers
);
const storeDataToSessionStorage = () => {
window.sessionStorage.setItem('redux', JSON.stringify(store.getState().users));
};
store.subscribe(storeDataToSessionStorage);
export default store;
so that user reducer always get initial values from the session storage. (you can push to localStorage also based on your requirement)
Solution 7:[7]
Please check these:-
Just you need to add one condition
import { AUTH_USER, UNAUTH_USER, AUTH_ERROR } from '../actions/types';
export default function(state = {}, action) {
switch (action.type) {
case AUTH_USER:
return { ...state, error: '', authenticated: localStorage.getItem('token') ? true : false }; // just add this condition here
case UNAUTH_USER:
return { ...state, authenticated: localStorage.getItem('token') ? true : false }; // just add this condition here also
case AUTH_ERROR:
return { ...state, error: action.payload };
}
return state;
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Ashad Nasim |
| Solution 2 | |
| Solution 3 | Dane |
| Solution 4 | Piotr Pliszko |
| Solution 5 | |
| Solution 6 | Sunil Kumar |
| Solution 7 | saddam |
