I have a page in my application that is an interactive chart with a bunch of settings (filters, time ranges, etc). I'd like to store them in-app state because some of the settings may be used by other components on another page, but right now if I click on any other tab and come back to the previous tab then the page shows the initial state(chart is gone, filtered data gone, date range showing the default value, dropdowns shows default ass well). And the state is also showing null.
Anything in your component state is dynamic; i.e., it is temporary. If you refresh the window, the old state is lost. A similar thing happens when you open a fresh tab—you get the state declared in your constructor. You can use any of the following if you want the state data in another tab:
Simply using redux won't solve your problem as redux store is also related to a specific browser tab. If you would like to persist your redux state across a browser refresh, it's best to do this using redux middleware. Check out the redux-persist, redux-storage middleware.
If you are using react-router you can simply pass required state through the link when you open a fresh tab. Here's an example:
<Link to={{
pathname: '/pathname',
state: { message: 'hello, im a passed message!' }
}}/>
Simply store the data in localStorage and access localStorage in other tabs.
If you are looking to use a variable across the entire application you can also use localStorage
localStorage.setItem('move', this.state.move);
also don't forget to unset it when you are done!
localStorage.removeItem('move');
The straight forward solution to this is redux-state-sync. It will just work and the store will be updated on all tabs and even windows on the browser.
I think you should implement react-redux into your app.
React-redux is a state management tool that manage the state of your application at a centralized location and you access data from store at anytime from anywhere.
React-redux:
https://redux.js.org/basics/usage-with-react
You some library for state management. The most popular one that's used with React is redux.
https://redux.js.org/introduction
Related
I have a doubt regarding dynamically rendering the logged in user's name in the NavBar in react.
So my app is structured in the following way
<App>
<Header/>
<Router>
....
<Router/>
<Footer/>
<App/>
Now inside the header component, I need to display "Welcome <user>" whenever the user is logged in. Here are some solutions that I thought of,but Not sure if I'm missing out on anything.
To send it as a prop to the header. But it can only be dynamic if I do a setState in the App component and re render the entire app component (Which I want to avoid)
To store it in redux, so header can dynamically get the updated. But since I am using JWT, and the user needs to stay logged in, If the user refreshes the page, store content will be wiped out.
I can store the username in localstorage as well, but the entire app would still need to be re rendered in order to display the change
-) I think it can be accomplished in Angular by BehaviorSubject, but in this scenario, I'm clueless.
Is there any solution or method that I am missing?
Thanks in advance
At first if you want to keep your user logged in then you have to store the JWT somewhere. Here by 'somewhere' I mean, so that you can access it on next reload.
In my maximum project I store my JWT in local-storage. It works something like this
When user logs in I store the JWT in local-storage also in my reducer.
Then on my 'Inital State' I always take the local-storage so that if we have anything in the local-storage it means user is logged in
initial_state = {
userName: localstorage.getItem('user_name')
}
That's how you can keep your user logged in and at the time of logout erase the local-storage as well as the reducer.
You can send the user as a prop to the header. That is kind of the preferred method. Have the user login and save their app state with redux of the useState hook that is now available in React.
To your second question, JWTs preserve session data. Rerendering the page won't cause that to disappear. Well-designed apps have JWT with an expiration time (usually 20 minutes or less).
I worked on a voting application that did exactly what you're looking for and did not require any global state management. Feel free to pick through it. It's an open-source project. I think we used Auth0 or Google's Oauth library to do authentication.
Here's the code for the React header(navbar): https://github.com/poll-pal/poll-pal/blob/master/src/Components/Header/Header.js
Here's the code for the Express server that backed it: https://github.com/poll-pal/poll-pal/blob/master/server/routes/auth.js
In react-admin, All I know is that the framework provides two ways for me to update, they are setting dataProvider refresh to true and refreshbutton. Now my situation is that I have a dialog which used to pay. And the pay request is not restful api. So I have to use 'fetch'. And after pay, How can I refresh the current View to get lastest data?
You can import and dispatch Redux action "refreshView" from 'ra-core', example:
https://github.com/marmelab/react-admin/blob/master/packages/ra-ui-materialui/src/button/RefreshButton.js
please provide more details. Also, view is auto refreshed when state/prop is updated!
In rare cases, forceupdate() can be used
I'm using React and Redux for my SPA and I want to store some data locally. And I need to synchronize appState with localstorage, so my data won't be lost after refreshing the page.
I'm completely new for React and Redux and have not much of understanding what's going on, but as I think Redux creates for me that state of entire app, so I can't just bind my state with localstorage in app component because it'll be just state of component and not of my app.
I would suggest storing the data in local storage with the following commands.
Set the data in localStorage can be done with the command:
localStorage.setItem('nameForData', variableNameForData);
To retrieve the data when required.
var variableNameForData = localStorage.getItem('nameForData')
To remove the data from localStorage:
localStorage.removeItem('nameForData')
These would typically be put inside action creators with a dispatch to change the state of some Boolean that tracks the applications interaction with localStorage.
For example you might have a state that is set to true when the local storage is created.
On refresh you might call an action creator that checks the local storage exists, and if it does set that boolean back to true or if it does not exist you are back to creating local storage and then set it to true.
You could put this function in componentWillMount(){} and it will be called when the component is first rendered, and thus in the case of a refresh.
Docs for component life cycle and specifically componentWillMount here
Docs for local storage here
I can suggest you to store the state after each action. For that you can use a localStorage middleware that only store the whole state object.
Then in your createStore part you will retrieve the initialState from the localStorage API.
In that case you don't need to modify any component, juste the initialState from localStorage and the middleware that save the state
I am using React with react-router and Reflux as my datastore, but I am unsure on how to best deal with persistence to allow page refresh.
My components connect to the store with Reflux.connect, but since the store fetches the data from a backend, it is not available yet when the Components first initialize and render.
When the user enters my app from the start, then all this data is loaded in order and available when it needs to be, but if further down a route you trigger a page refresh, react tries to render components that rely on data that is not there yet.
I solved this by constantly keeping a copy of data in LocalStorage and serving that from the Reflux store getInitialState(), so that all components get the data before they render.
I wonder if this is the proper way to do it. When for some reason the local storage data gets cleared or corrupted, the interface goes blank, because the components cannot access the correct data. Substructures and properties don't exist and trigger javascript errors. It seems like a messy and unreliable solution.
I am curious to know what patterns are used to solve this.
------ edit -----
To answer to the comment of WiredPrairie:
1) Why are you initializing components with data in getInitialState?
When my components use Reflux.connect, they don't have the data in their state yet on the first render as the store still needs to fetch its data. My views currently don't work gracefully with undefined data. By returning the locally stored cache from the Reflux store in getInitialState(), all connected components will get that data before their first render call.
2) What's causing a page refresh and why can't the data be loaded in the same manner as it was the first time?
It's mainly a workaround I had to build around livereload refreshing the page when I make edits (will look into using react-hotloader later but is not an options yet), but users can also just hit refresh when they are somewhere in my nested views and that would have the same effect. When they manually refresh, they are not entering the app at the start.
3) When components are wired to the change events of a store, why don't they update then?
They do update, but like I said they don't deal with empty data right now and on first render they will miss it waiting for the store to fetch things. I can make all my views work gracefully with empty data, but that would add a lot of boilerplate code.
From the replies so far, I get the feeling that what I'm doing with localStorage is the common way to do it. Cache stuff locally in localStorage or sessionStorage or something similar, and serve that data immediately after a refresh.
I should make my views a bit more robust by gracefully handing empty data on the odd occasion that localStorage doesn't work properly.
I've been caching each Store in sessionStorage when its emitChange() fires, and initializing the store from sessionStorage if cached data exists, or null values otherwise. This seems to work provided that the views can handle null values, which is probably a good idea anyway (it sounds like this is your main problem).
I'd suggest making your views handle the case of no data gracefully, initialize everything with null values if the cache isn't available, and then call the backend to update the null values with an Action whenever the data returns.
I haven't tried Reflux, but in regular Flux it would look like this (maybe you can apply the same principle):
var _data;
if (sessionStorage.PostStore)
_data = JSON.parse(sessionStorage.PostStore);
else {
_data = {
posts: null
};
BackendAPI.getPosts(function(err, posts) {
if (posts) {
PostActions.setPosts(posts);
}
});
}
...
AppDispatcher.register(function(payload) {
var action = payload.action;
switch (action.actionType) {
...
case Constants.SET_POSTS:
_data.posts= action.data.posts;
break;
default:
return true
}
// Update cache because state changed
sessionStorage.PostStore = JSON.stringify(_data);
PostStore.emitChange();
return true;
});
I've got a phonegap-wrapped sencha touch (v1.1.1) app that has a few stores, and a list.
The problem occurs after you're logged in as "User1", and then log out and log back in as "User2". The list won't refresh with the new account data fetched by the store proxy.
I'm currently attempting to call .refresh() on the List itself, which according to the docs will also cause the store to refresh its data (although, I'm manually doing that before hand anyway)
var sL = Ext.getCmp('AccountsList');
sL.refresh();
sL.scroller.scrollTo({x:0,y:0});
We've also tried to .sync() the stores with no results.
Ext.getStore('AccountsTransfersTo').sync();
Ext.getStore('AccountsTransfersFrom').sync();
Ext.getStore('AccountsStore').sync();
Any idea what the problem is, or has anyone run into something similar?
refresh won't reload the store, it just grabs whatever is in the store and re-renders the view.
sync is for updating the store when you've got local changes and you're posting them to the server.
You need to call the load method on the store.
It sounds like a caching problem. or a state problem.
a link:
http://html5wood.com/sencha-touch-difference-between-ext-get-and-ext-getcmp/
from the page:
Sencha Touch: Difference between Ext.get() and Ext.getCmp()
Important: It returns the same element object due to simple caching on retrieving again. It means it returns wrong element on second time. Ext.fly() can be used for avoiding this problem.
hope it helps
mike.
You may Load the store each time
var sL = Ext.getStore('AccountsTransfersTo')
sL.load();
sL.scroller.scrollTo({x:0,y:0});
or you may try to clear data form store and reload it again
var sL = Ext.getStore('AccountsTransfersTo')
sL.removeAll();
sL.load();
sL.scroller.scrollTo({x:0,y:0});