useSelector and dispatch using persist redux - reactjs

I am using persist redux to save the state of some states after refresh.
However, I'm having trouble accessing the values using these tools.
For example, using a simple redux toolkit I can simply do:
const userId = useSelector((state) => state)
However, when using a persist reducer, it returns undefined.
Here's my code for store:
import { configureStore } from '#reduxjs/toolkit';
import idReducer from './idSlice'
import { persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' // defaults to localStorage for web
import thunk from 'redux-thunk'
const persistConfig = {
key: 'root',
storage,
}
const persistedReducer = persistReducer(persistConfig, idReducer)
let store = configureStore({
reducer: persistedReducer,
devTools: process.env.NODE_ENV !== 'production',
middleware: [thunk]
});
export default store;
index.js:
const root = ReactDOM.createRoot(document.getElementById("root"));
let persistor = persistStore(store)
root.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>
);
Here's what I have for dispatch (doesn't seem to work either)
const handleClick = (id) => {
console.log("dispatch ", id)
dispatch(setId(id))
}
Thanks!

You said
I am using persist redux to save the state of some states after refresh.
Redux does not keep anything after refresh. Its store lives in JavaScript, so when the code is triggered to run again, everything restarts.
The first important thing is to consider not refreshing at all. I mean keep a high order component, and re-render the components below it.
The second way is to save the necessary values on localStorage, or sessionStorage (the difference is that while data in localStorage doesn't expire, data in sessionStorage is cleared when the page session ends, found here) or on your database on the backend.
Normally user's data (not risky data, like token) is saved on localStorage so user wouldn't have to sign in every time.

Related

Using Redux-Persist with REST API

I have followed countless tutorials on redux-persist and believe I am implementing it correct in my redux-toolkit app. However, I think there's a disconnect in fundamental understanding on my end. Can I use persist on an application that uses REST API or do I need to setup my on backend server for it to work? And if you cant use persist for an app that uses REST API, how would I go about getting state to persist on an app that uses Redux-Toolkit? In Application in my browser's devtools, it shows that my state has been saved but when I close the browser window and open it back up, I find that my shopping cart is empty. Here's my code just in case I'm missing something:
Store js
import cartReducer from "./features/Cart/cartSlice"
import modalReducer from "./features/Modal/modalSlice"
//Persist
import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
const persistConfig = {
key: "persist-key",
storage
}
const persistedReducer = persistReducer(persistConfig, cartReducer)
const store = configureStore({
reducer: {
persistedReducer: persistedReducer,
cart: cartReducer,
modal: modalReducer,
},
middleware: (getDefaultMiddleware) => getDefaultMiddleware({
serializableCheck: false
})
})
const persistor = persistStore(store)
export default store;
export { persistor }
Index js
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import store, {persistor} from "./store"
import { PersistGate } from 'redux-persist/integration/react';
import { Provider } from 'react-redux';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<PersistGate persistor={persistor}>
<App />
</PersistGate>
</Provider>
</React.StrictMode>
);
Any help will be appreciated.
Yes you can use redux-persist to able keep data at client side,
but keep on mind, you should never keep sensible data. And use it only keep needful data such as session data settings, etc.
You can retrieve all such as normal redux data, handler as useSelector from react-redux library will return your current state.
So your backend not implements nothing to warranty your redux flow, it is entirety responsibility of frontend.

Way to keep login state using JWT after logged in

I'm wondeirng is there a original, and most used way to code "Way to keep logged in state".
Currently, I set log in button to 'MyPage' button after successful logged in.
However, i referesh the web, then this state has gone.
Ah, i'm using React, and using Cookies with Access and Refresh Token, and wondering "Should i use another state to keep logged in information or not."
Before, i used localStorage and set the state manually, such as "isLogin" : "true" state in localStorage.
However, i'm wondering the way to use accessToken which expires automatically in 30 mins.
Will there ve a neat way to construct this function?
Thanks !
Do you want to log in again after the relaunch of the browser?
If so, it can be done with Redux store.
Here is the link for documentation.
https://redux.js.org/api/store
If not, it should be done with session or chrome.storage or something.
You can use redux-persist in your application so that state is maintained even if you are refreshing page after logged in application.
Sample redux store configuration:
import { combineReducers} from 'redux';
import { configureStore } from '#reduxjs/toolkit';
import thunk from 'redux-thunk';
import { persistReducer } from 'redux-persist';
import storage from '#/redux/sync_storage';
import { setupListeners } from '#reduxjs/toolkit/dist/query';
import persistStore from 'redux-persist/lib/persistStore';
import app from '#/redux/slice/appSlice';
const persistConfig = {
key: 'root',
storage,
//blacklist: ['app']
}
const rootReducer = combineReducers({
app,
})
const initialState = {}
const persistedReducer = persistReducer(persistConfig, rootReducer)
const store= configureStore({reducer:persistedReducer,initialState,devTools: process.env.NODE_ENV !== 'production',middleware:[thunk]})
setupListeners(store.dispatch);
export const persistor = persistStore(store, {}, () => {
persistor.persist();
});
export default store;
The below urls will help with the configuration for react application
https://www.npmjs.com/package/#reduxjs/toolkit
https://www.npmjs.com/package/redux-persist
https://kandi.openweaver.com/javascript/rt2zz/redux-persist#Code-Snippets

Best way to check if user is logged-in when he opens the website

I have a react app that uses sessions with an express server using REST API.
In my Redux store(on the front end) I store "isLoggedIn" in the redux state. Based on that property I show either the login page , or the home page. Now when I open the website, the initial redux "isLoggedIn" state is false, so the question is, how do I check if the user is actually logged in. I am thinking about sending a request to the endpoint for fetching the data I need in the homepage, and if the user is not logged in, I would get an 401 response and then show the login page. Is this the correct approach?
Before I get into how you could persist your redux state I would advise(Note This is a personal view)
Going through the boiler plate code of setting that up is really unnecessarily long
Best way out for this is using express-session whereby the cookie is persisted for as long as you gave it e.g if you set cookie to be 3days it will stay for 3days
I so believe that it's industry standard working with httpOnly cookie for session handling as this is secure avoiding XSS attacks and CSRF attacks
Either way below is your solution to persisting Redux Store
So with persisting your redux state you can look into something like this
First run npm install redux-persist or yarn add redux-persist
Now time to create your redux store
So now when you create your redux store with createStore you want to pass your createStore function a persistReducer Once your store is created, pass it to the persistStore function, which will make sure that your redux state is persisted
Implementation below
import { createStore } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
import rootReducer from './reducers';
//Import above comes from a file **reducers** which has the combineReducers
// basically based on this code snippet
// I have my combineReducer object in another folder hope that it
//makes sense. Avoided to write it here to keep code short to show
//more of setup in store persisiting
const persistStoreConfigs = {
key: 'root',
storage: myAppStorage,
stateReconciler: autoMergeLevel2
};
const persistantReducer = persistReducer(persistStoreConfigs, rootReducer);
export const store = createStore(persistantReducer);
export const persistor = persistStore(store);
Now passing the store to the App level
import React from 'react';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/lib/integration/react';
// This are coming from the store code above that we made
import { persistor, store } from './store';
// This are basically your Custom Componentd
import { HomePage, LoadingView } from './components';
const App = () => {
return (
<Provider store={store}>
// the loading and persistor props are both required!
// Noting that the LoadingView is a custom component
// But the persistor isn't
//The main aim of this two props is to delay the rendering of your app UI until the persited state is retrieved hence the loading screen component
<PersistGate loading={<LoadingView />} persistor={persistor}>
<HomePage />
</PersistGate>
</Provider>
);
};
export default App;

Correct setup of redux-persist v5 with react boilerplate

I have been trying to setup redux-persist 5.9.1 with reactboilerplate 3.4.0 framework.
The error I receive seems to related to redux-immutable and I am unable to figure out right configuration.
Here is what I have done so far:
1. Install NPM
npm i -S redux-persist redux-persist-transform-immutable
package.json
"redux-persist": "^5.9.1",
"redux-persist-transform-immutable": "^5.0.0",
2. Setup Redux Persist in store.js
//store.js
import .... (other usual stuff)
import { persistStore, persistReducer } from 'redux-persist';
import storageSession from 'redux-persist/lib/storage/session';
import immutableTransform from 'redux-persist-transform-immutable';
const persistConfig = {
transforms: [immutableTransform()],
key: 'root',
storage: storageSession,
}
const rootReducers = createReducer();
// Using persistReducer not persistCombineReducer because the rootReducer is already returned by combinedReducer from redux-immutable.
const persistedReducer = persistReducer (persistConfig, rootReducers)
export default function configureStore (initialState = {}, history) {
// other usual stuffs ...
// I modified how store is created using persistedReducer
const store = createStore(
persistedReducer, // this line used to use createReducer() method
fromJS(initialState),
composeEnhancers(...enhancers),
);
const persistor = persistStore(store);
return { persistor, store };
// Please note, I have commented out hot reloading of reducers for now.
}
3. No change in reducers.js
4. Update App.js
import 'babel-polyfill';
import React from 'react';
// Added below
import { PersistGate } from 'redux-persist/es/integration/react';
// other usual setup
// Line below used to define just store but now we are defining persistor and store
const { persistor, store } = configureStore(initialState, browserHistory);
// Finally, update the render method:
const render = () => {
ReactDOM.render(
<Provider store={store}>
<PersistGate persistor={persistor}>
<Router
history={history}
routes={rootRoute}
render={
applyRouterMiddleware(useScroll())
}
/>
</PersistGate>
</Provider>,
document.getElementById('app')
);
};
And still no luck:
Error:
I think I do not have immutable maps configured right. Any help?
The way you doing is correct as documentation.
The problem is in here:
const rootReducers = createReducer();
// Using persistReducer not persistCombineReducer because the rootReducer is already returned by combinedReducer from redux-immutable.
const persistedReducer = persistReducer (persistConfig, rootReducers)
This const rootReducers = createReducer(); should not call like that, it will trigger the function. You should put like const rootReducers = createReducer; or better call like this:
const persistedReducer = persistReducer (persistConfig, createReducer)
Please see documentation, not call rootReducer for trigger function but pass it as variable.

React Redux Refresh issue

I am using react-redux and I am having a issue where I loose my redux state when the page is refreshed.
Now before I go further on that this is the scenario, which I may be messing up.
Question one: Can I connect to more than one layout?
I have a dashboard and a "app" layout. Both have separate layouts. I connect both in the same manner:
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actionCreators from '../actions/actionCreators';
function mapStateToProps(state) {
return {
profile: state.profile,
child: state.child,
}
}
function mapDispachToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch);
}
const LayoutApp = connect(mapStateToProps, mapDispachToProps) (DashboardLayout);
export default LayoutApp;
The dashboard connects just fine. I am able to hit reducers when i need to and update the store, however the dashboard links to the app for certain parts of data manipulation for you to play with. When it links I get the data in props as expected, however as soon as any page refreshes within the app layouts I loose the props being sent by maptoprops.
I have tried to combine into one master layout however that seems to have the same affect. I have also tried to save to state immediately when i first receive data but that seems to be lost as well upon refresh which makes me think it is resetting it.
Summary:
- DashboardLayout (connects to redux)
- AppLayout (connects to redux) however after a page refresh it looses props to the Applayout and needed data is gone.
Get to know redux-persist
https://github.com/rt2zz/redux-persist
You can install it using
npm i --save redux-persist
persistStore is the function that allows you to persist store.
import {persistStore} from 'redux-persist'
and autoRehydrate is the action that is performed whenever the state needs to be rehydrated
import {autoRehydrate} from 'redux-persist'
following is the structure that may be useful.
import {compose, applyMiddleware, createStore} from 'redux'
import {persistStore, autoRehydrate} from 'redux-persist'
// add `autoRehydrate` as an enhancer to your store (note: `autoRehydrate` is not a middleware)
const store = createStore(
reducer,
undefined,
compose(
applyMiddleware(...),
autoRehydrate()
)
)
// begin periodically persisting the store
persistStore(store)
and then for your reducer
import {REHYDRATE} from 'redux-persist/constants'
//...
case REHYDRATE:
var incoming = action.payload.myReducer
if (incoming) return {...state, ...incoming, specialKey:
processSpecial(incoming.specialKey)}
return state
following are the methods which can be used to work out of redux-persist
persistor.pause() - pauses redux persist
persistor.resume() - resumes redux persist
persistor.purge() - deletes all persisted data
persistor.rehydrate() - calls reducer to rehydrate store
types of storage redux persist allows
// sessionStorage
import { persistStore } from 'redux-persist'
import { asyncSessionStorage } from 'redux-persist/storages'
persistStore(store, {storage: asyncSessionStorage})
// react-native
import {AsyncStorage} from 'react-native'
persistStore(store, {storage: AsyncStorage})
// web with recommended localForage
import localForage from 'localforage'
persistStore(store, {storage: localForage})
it is the most basic use redux-persist hope it helps.
You can connect to as many components as you want to same store properties - that's not issue. Just to note here, connect is listening for changes so when action changes store it would cause all components listening to changed store parts to re-render (or at least do computation and then compare shadowdom).
Store is stored only when running application - after page close/resfresh it's cleared.
There is good thread about persistence of store (eg. to local storage): Where to write to localStorage in a Redux app?
As mentioned in the previous answers storing the state in local storage is one possible solution however some other possible solutions are
Make sure your links use the Push API ie <Link to={"route"}/> (react-router) this will ensure the store is not removed between route changes
Set your initial state when you create your store
const rootReducer = combineReducers({
todos: todos,
visibilityFilter: visibilityFilter
});
const initialState = {
todos: [{id:123, text:'hello', completed: false}]
};
const store = createStore(
rootReducer,
initialState
);
This will hydrate your reduce store when it is initialized
There are some other libs/frameworks/boilerplates that you could use that also implement server rendering with will render the HTML to the page on the initial render and then load React once the DOM has loaded some of this libs are
Next.js,Electroide,react-redux-universal-hot-example
Going down this route will add significant complexity to your app so you should weigh up the pros and cons before embarking down this route
I had this problem, so as somebody mentioned here, i used the localStorage.
In my reducer, i did like this:
const exampleKey = "_exampleKey"
const INITIAL_STATE = {
example: JSON.parse(localStorage.getItem(exampleKey))
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case 'SET_TRUE':
localStorage.setItem( exampleKey, JSON.stringify(true));
return { ...state, example: true };
case 'SET_FALSE':
localStorage.setItem( exampleKey, JSON.stringify(false));
return { ...state, example: false};
default:
return state
}
Pointing INITIAL_VALUES.example to localStorage's item example ensure us to keep the correct value when the page reloads.
Hope it helps somebody.

Resources