I"m having issues fully implementing authentication with a JWT token. I finally have my app keeping me signed in on a page refresh...HOWEVER, I loose the rest of the state of my user. So my user is still signed in, but has no username, or any kind of info.
Here is how I'm using the token to keep a user logged in.
const token = localStorage.getItem('token');
if (token) {
store.dispatch({ type: 'AUTH_USER'})
}
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
Then my currentUser reducer has this case...
case 'AUTH_USER':
return { ...state, loggedIn:true }
The problem is on a page re-render state gets set back to an empty object. I suppose I could send a request back to the API to fetch the user data again...BUT, there has to be a way to keep that user's data stored???
I am using redux-persist to make the state persistent. Recommended. I had, however, some difficulties making it work.
Here is my code if you want to use it.
I am using redux-persist version 4 (version 5 had some major changes, didn't migrate to it yet), so please use this version if you want to use my code.
I am also using Redux DevTools, react-router and redux-thunk, therefore the code includes them.
These are the relevant parts from my index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { compose, createStore, applyMiddleware } from 'redux';
import { persistStore, autoRehydrate } from 'redux-persist';
import ReduxThunk from 'redux-thunk';
const composeEnhancers =
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
}) : compose;
const enhancers = composeEnhancers(
applyMiddleware(ReduxThunk),
autoRehydrate()
);
const initialState = {};
const store = createStore(
reducers,
initialState,
compose(enhancers)
);
persistStore(store);
ReactDOM.render(
<Provider store={store}>
<div>
<BrowserRouter>
<Switch>
<Route exact path="/xxx" component={xxx} />
<Route path="/yyy" component={yyy} />
</Switch>
</BrowserRouter>
</div>
</Provider>
, document.querySelector('#root'));
Good luck!
Related
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.
I am using redux persist in my web application to store data in localStorage but redux lose data on page reload. does anybody have the same issue or anybody can help me with this.
my redux-persist initialization is:
import { createStore } from "redux";
import userData from "./reducers/reducers";
import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
const persistConfig = {
key: "root",
storage,
whitelist: ["userData"],
};
const persistedReducer = persistReducer(persistConfig, userData);
const store = createStore(persistedReducer);
export const persistor = persistStore(store);
export default store;
while my index.js:
import React from "react";
import ReactDOM from "react-dom";
import "./index.scss";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/lib/integration/react";
import { persistor } from "./redux/store";
import store from "./redux/store";
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<PersistGate persistor={persistor}>
<App />
</PersistGate>
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
reportWebVitals();
I am unable to find any bug or bad practice can anyone help me.
Redux-persist will handle all the stuff. redux-persist
Example :
configureStore.js
import { createStore } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' // defaults to localStorage for web and AsyncStorage for react-native
import rootReducer from './reducers'
const persistConfig = {
key: 'root',
storage,
}
const persistedReducer = persistReducer(persistConfig, rootReducer)
export default () => {
let store = createStore(persistedReducer)
let persistor = persistStore(store)
return { store, persistor }
}
App.js
import { PersistGate } from 'redux-persist/integration/react'
const App = () => {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<RootComponent />
</PersistGate>
</Provider>
);
};
I don't see anything 'wrong' with your code, the only difference is that you dont have a rootReducer, also you dont need to put a whitelist, since when I used whitelist it didn't filter the others, at least it didn't work for me, what did work for stating which reducer should/shouldn't keep storage was using blackList and passing the reducers that wasnt working.
Try giving a shot creating a rootReducer.
I've experienced it too, make sure the data you want to persist is by calling the reducer that would put a value on the state. ex of authToken.
can anybody please tell me why is my redux app not working? I have been going step by step from tutorial from youtube but my state isn't event shown in redux dev tools. All I have there is 'states are equal'.
counter.js file (reducer)
const counterReducer = (state = 0, action) => {
switch (action.type) {
case 'INCREMENT':
return state + 1;
default:
return state;
}
}
export default counterReducer;
index.js file
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
//reducer
import counterReducer from './Reducers/counter';
//store
import {createStore} from 'redux';
//provider
import { Provider } from 'react-redux';
const store = createStore(
counterReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
What Am I doing wrong?
try this code below in your index.js file.
import { createStore, compose } from 'redux';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
counterReducer,
composeEnhancers()
);
and if you are using any middleware like redux-thunk, then do as following,
import { createStore, applyMiddleware, compose } from 'redux';
const store = createStore(
rootReducer,
composeEnhancers(applyMiddleware(thunk))
);
If you want to update anything inside redux store then you have to dispatch an action mandatorily. Create an action, dispatch that action from your component. Then you will see everything working.
The above rated solution is accurate, but we have some cases when we don't want the end users to see our state using React DevTools. For example, in a production environment, we don't want this to happen. To implement this functionality use this chunk of code.
import { createStore, compose } from 'redux';
// if env is not equal to 'production', show state in Redux DevTools
const composeEnhancers = (process.env.REACT_APP_NODE_ENV !== 'production' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose;
const store = createStore(counterReducer,composeEnhancers());
// rest of the code goes here...
My solution was to change from "Autoselect instances" in the right upper corner to my own instance. The state was 'hidden' and that's why I couldn't see it despite configured everything correctly.
So I have been creating an application where a user needs to log into firebase using google authentication. I am using redux, react-redux, react-redux-firebase, redux-firestore, and redux-thunk. I am able to successfully log the user into firebase with the google authentication. I now want to use firestore in order to have a collection of all the users. I have looked at the documentation for redux-firestore and the method of getting/manipulating is a little different. I have tried using the documentation, but I cannot get the functions to work with redux-firestore.
Here is the action
export const signIn = () => (
dispatch,
getState,
{getFirebase, getFirestore}) => {
const firebase = getFirebase();
const firestore = getFirestore();
firebase.auth().signInWithPopup(provider).then(function(result) {
if(result.credential) {
firestore.get({collection: 'users', doc: result.user.uid}).then(function(doc) {
if(!doc.exists){
console.log("new!")
firestore.add(
{collection: 'users', doc: result.user.uid},
{name: firebase.auth.currentUser.displayName});
} else{
console.log("old!")
}
})
}
}).catch((err) => {
})
};
And here is my setup in index.js for the src folder
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {BrowserRouter} from 'react-router-dom';
import {createStore, applyMiddleware, compose} from 'redux';
import {Provider} from 'react-redux';
import allReducers from './reducers';
import thunk from 'redux-thunk';
import firebase from './Firebase';
import {firebaseConfig} from './Firebase'
import {createFirestoreInstance, getFirestore, reduxFirestore} from 'redux-firestore';
import {ReactReduxFirebaseProvider, getFirebase} from 'react-redux-firebase';
const store = createStore(
allReducers,
compose(
applyMiddleware(thunk.withExtraArgument({getFirebase, getFirestore})),
reduxFirestore(firebaseConfig)
));
const rrfConfig = {
userProfile: 'users',
useFirestoreForProfile: true
};
const rrfProps = {
firebase,
config: rrfConfig,
dispatch: store.dispatch,
}
ReactDOM.render(
<Provider store={store}>
<ReactReduxFirebaseProvider {...rrfProps}>
<BrowserRouter>
<App />
</BrowserRouter>
</ReactReduxFirebaseProvider>
</Provider>,
document.getElementById('root')
);
serviceWorker.unregister();
I know that I have not used createFirestoreInstance in this code, but I was playing around with it.
If anyone could tell me how to get this working, I would appreciate it.
Thanks!
Quick update:
I have figured out how to at least write to firestore using this code
const userRef = firebase.firestore().collection('users').doc(result.user.uid);
userRef.get().then(function(doc) {
if(!doc.exists){
userRef.set({name: result.user.displayName});
}
})
This is not the best (or maybe the right solution), but it does work. It is not using redux-firestore, but is there a better way?
If you're using React, use react-redux-firebase. There's no need for these many complication and the code looks much neater and simpler. Authentication, firestore and all other firebase features works out of the box with just small amount of code. They also comes with React hooks like useFirebase() and useFirestore() instead of you needing to write them on your own.
react-redux-firebase is built on top of redux-firebase and provides all the things you would need in React.
If your app only uses firebase, I would even recommend you use just plain Redux without Redux Thunk or Redux Saga.
When I navigate through the app the UI is stuck although the url changes.
I would like to integrate redux-persist on my current app but it eventually drove me to a strange bug to me.
Note: I use also the redux-saga as middleware on creating the store.
store.js
import { createStore, applyMiddleware, compose } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' // defaults to localStorage for web and AsyncStorage for react-native
import createSagaMiddleware from 'redux-saga'
import rootReducer from "../reducers/index";
import rootSaga from '../sagas/index'
const persistConfig = {
key: 'root',
storage,
}
const persistedReducer = persistReducer(persistConfig, rootReducer)
const sagaMiddleware = createSagaMiddleware()
const middleware = applyMiddleware(sagaMiddleware)
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore(
persistedReducer,
{},
composeEnhancers(middleware)
)
export const persistor = persistStore(store)
sagaMiddleware.run(rootSaga)
export default store
window.store = store
When I comment in the Persist Gate component then the navigation works as intended.
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { BrowserRouter as Router } from "react-router-dom";
import { Provider } from 'react-redux'
import registerServiceWorker from "./js/registerServiceWorker";
import { PersistGate } from 'redux-persist/integration/react'
import store, { persistor } from './js/store';
ReactDOM.render(
<Router>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>
</Router >,
document.getElementById("root")
);
registerServiceWorker();
I hope I made myself clear!
Try wrapping your Router with the PersistGate. The order of these higher order components matters for React Router. The way you have it now, when you change the url it's not triggering a re-render, so swapping the order should fix the issue.