Is the ngrx store persistent? In other words can we close the browser reopen it, and retrieve state that was added to the ngrx store?
Currently ngrx/store doesn't support such functionality. But you can maintain state by using a library like ngrx-store-persist to save data to the browsers indexedDB, localStorage or WebStorage. This library automatically saves and restores ngrx/store's data. You just need to add redux keys that you want to store in the config (see "Usage" section).
The nrxg/store is in memory only. To manage state via the browser with something like pouchdb ngrx/effects can be used. More here.
You can maintain state by using a library like idb.js to save data to the browsers indexDB, then by using the ngrx effect you can have an init effect to reload the state back when webapp loads. here is a example code let say I want to reload selected language. the effect would be:
#Effect()
init$: Observable<Action> = defer(() => {
return from(storageDb.readData('app', Lang.ActionType.LANG_KEY)).pipe(
map((data: any) => {
const tmpData = data ? data.state : {dir: 'rtl', selected: 'ar'};
this.translate.setDefaultLang(tmpData.selected);
return new Lang.LanguageInit(tmpData);
})
);
});
the storageDb.readData is a wrapper for idb to load data by key, the effect will kick in when effects are getting loaded it will get the data from the indexdb and if it does not find any sets default value then dispatchs an Action. ngrx best practices is to use promises in effects not in reducers since loading the data is async operation, then in your state reducer you can catch the Action like so:
case lang.ActionType.LANGUAGE_INIT: {
return {...state, ...action.payload};
}
Using it in this way you can slice your states save them and loaded them even when lazyloading.
Related
In a Sapper app, I want to be able to persist the state of some UI components so I can navigate the app without losing state when the user returns to the pages using those components.
In a Svelte-only app, this is usually done with a custom store that uses the sessionStorage or localStorage API. A good example of that can be found in R. Mark Volkmann's book Svelte and Sapper in Action, ยง6.24:
store-util.js
import {writable} from 'svelte/store';
function persist(key, value) {
sessionStorage.setItem(key, JSON.stringify(value));
}
export function writableSession(key, initialValue) {
const sessionValue = JSON.parse(sessionStorage.getItem(key));
if (!sessionValue) persist(key, initialValue);
const store = writable(sessionValue || initialValue);
store.subscribe(value => persist(key, value));
return store;
}
Unfortunately, using stores that way breaks immediately in Sapper because the scripts run on the server first, where sessionStorage is not defined. There are ways to prevent some parts of code from running on the server (using the onMount lifecycle function in a component, or checking process.browser === true), but that doesn't seem possible here.
Persisting some state locally looks like a very common use case so I'm wondering what's the right way to do it in a Sapper app (considering that I haven't even found the wrong way).
Provide a dummy store for SSR.
It is always possible to do feature detection with something like typeof localStorage !== 'undefined'.
Your component code will re-run in the browser, even if the page was SSR'd. This means that if it is fed a different store, the browser-only values will take over and update existing state (inherited from the server).
See this answer for an example.
I have some state with React recoil, but whenever the page is manually refreshed, the recoil state is reset.
Is this normal behaviour, because i know other state management libraries like flux and react-redux will do this.
Is it best practise to save it into localStorage to actually have it persisted in the browser (because localStorage is also a synchronous api, so that could definitely also cause some issues.)
Even though it is a fairly new library, is there some way to persist state even on manual page refreshes?
I have some state with React recoil, but whenever the page is manually refreshed, the recoil state is reset.
Yes, it's normal behavior.
Is this normal behavior because I know other state management libraries like flux and react-redux will do this.
Yes, only a subset of the state management libraries persists the data themselves. It's more common to find external libraries that do that or custom solutions.
Is it best practice to save it into localStorage to actually have it persisted in the browser (because localStorage is also asynchronous API, so that could definitely also cause some issues.)
It depends on your needs:
do you want to offer a full offline experience? Go for it
are you concerned by the performance implications in case of big amount of data? You could consider indexedDB
are you just trying to persist some auth tokens? Probably sessionStorage could be the right solution
does the server need to know the stored data? You could opt for cookies
Let's say that without knowing your use case it's hard to suggest something ๐
Even though it is a fairly new library, is there some way to persist state even on manual page refreshes?
Yes, with useRecoilTransactionObserver_UNSTABLE you could get notified of every Recoil change and persisting the data. Then, with RecoilRoot' initializeState you could restore it. As you said, the library is new and the APIs could change rapidly in the next months ๐
You could use recoil-persist library to persist state to localStorage.
Here how you could use it:
import { recoilPersist } from 'recoil-persist'
const { persistAtom } = recoilPersist()
const counterState = atom({
key: 'count',
default: 0,
effects_UNSTABLE: [persistAtom],
})
This works nicely, from the docs here.
Any change to the order state gets written to localStorage, and the value from localStorage is read into the order state on refresh.
// define this function somewhere
const localStorageEffect = key => ({setSelf, onSet}) => {
const savedValue = localStorage.getItem(key)
if (savedValue != null) {
setSelf(JSON.parse(savedValue))
}
onSet(newValue => {
localStorage.setItem(key, JSON.stringify(newValue))
})
}
// this is an example state atom
export const orderState = atom({
key: 'orderState',
default: {
store: {}, // { id, name, phone, email, address }
items: {}, // { [itemId]: { id, name, sizeTable, quantity, size } }
contact: { deliveryOption: 'ship' }, // { name, email, phone, address, city, state, zipcode, promotions, deliveryOption }
},
// add these lines to your state atom,
// with the localStorage key you want to use
effects_UNSTABLE: [
localStorageEffect('order'),
],
})
I want to save data of every reducer in localStorage in the form of key value pairs
UserReducer: {isLoding: true, ....}
OrderReducer : {isRecieved: false, ....}
So if there is any changes happen in any of the reducer I want to set the updated data in local storage.
What I am thinking is I can do this store.subscribe but how I could know that which reducer has been changed so I can set the whole reducer data to localStorage.
Need help. Thanks :)
One way to do it is to create a lastAction reducer which will store the value of the last dispatched action. Then in store.subscribe you could do:
let nextState = store.getState();
let dispatchedAction = nextState.lastAction.type;
And when you know the last dispatched action, you can update the local storage depending on that action type.
You can create a redux middleware, it will get fired everytime an action is dispatched. Within the middleware you can put a conditional check to see if prev and next store are same. If not, you can update your local storage.
I found this helpful for creating a middleware. And it's fairly easy.
I think you could not know the changes from the store.subscribe.
The last place when you know the changes are the actions.
You can put the persisting logic to the reducers. But maybe you should not want to introduce side effect in your reducer.
So basically you have two options if you rule out the first one.
Persist the whole store as a single object to local storage. It is the easier option. I would try this solution first and I would measure the overhead with a worst case scenario.
Make a redux middleware and capture the individual actions. You can copy the reducer pattern to persist only the object that changed.
You can use subscribe for option 2. You can use middleware for option 2 and option 3.
I came up with one more solution.
You can do the persisting in your action creator with Redux Thunk or with Redux Saga.
You can use React Component for that. Connect it to the Store, and use it to write to localStorage instead of rendering something.
Imho, it would be easier to write than middleware.
You can use redux-watch to listen to the changes in redux store
for e.g.
// ... other imports/requires
import watch from 'redux-watch'
// assuming you have an admin reducer / state slice
console.log(store.getState().admin.name) // 'JP'
// store is THE redux store
let w = watch(store.getState, 'admin.name')
store.subscribe(w((newVal, oldVal, objectPath) => {
console.log('%s changed from %s to %s', objectPath, oldVal, newVal)
// admin.name changed from JP to JOE
}))
// somewhere else, admin reducer handles ADMIN_UPDATE
store.dispatch({ type: 'ADMIN_UPDATE', payload: { name: 'JOE' }})
this may help you.
I want to extend the approach 3 that #PeterAmbruzs shared. Actually the problem which I faced was whenever there was an action dispatched, store wasn't updated immediately but I still wanted to persist it so I imported reducer and passed state and action into it to get the required next state
import reducer from './reducer'
const someMiddleware = store => next => action => {
const state = store.getState(),
const nextState = reducer(state, action)
// Save it to Local Storage
// Or Make an API Call to save it to server
next(action)
}
I have a simple question about coding style for single page application. My front end is using React Redux
For example I have a standard CRUD page where data is displayed in table and pop up modal form. Data table is filtered from the server not from the client.
My question : If i create, update or remove a data should I call a refresh function or just edit it in redux store?
Refresh function :
Data always updated
Newly added data is filtered
Two times request, slower, unresponsive (Main problem)
Redux store:
App looks responsive
One time request
Lost server side filter function and data is not updated if multiple users is using the app (Main Problem)
Any advice will be appreciated
Edit the store locally to give immediate feedback, then send the request and when you get the reply back consolidate the store with the new data
basically, do both things and get the best benefit of both worlds
Dispatch an async action which queries the server where filter happens and when it resolves, update redux state with the refreshed, filtered data.
Pseudocode
// dispatches an action to refresh data without page reload
export function refreshDataAction() {
return (dispatch, getState) => {
return (
fetch('api/data', options) // fetch the data from server and let it filter
.then(data => dispatch(updateDataAction(data)))
);
};
}
// dispatches an action to update redux state with filtered data
export default function updateDataAction(data) {
return {
type: 'UPDATE_DATA',
...data,
}
}
Then you could just call dispatch(refreshDataAction()). Data is filtered, no page refresh.
Calling refresh in a React application (not only React, but any real-time front-end app) kind of defies whole principal of using React.
What you should do is, whenever there occurs a data-changing operation in your client, you should trigger an API call, that alters your server-side data accordingly. Send the data back to the client (you can send it to all clients, if you fancy web-socket), save it to the Redux state to trigger a re-render.
I started using ParseReact (https://github.com/ParsePlatform/ParseReact), but i want to know if there are any way of realtime data ? Like in MeteorJS or Firebase.
To add Parse data to a component, it simply needs to subscribe to a standard Parse Query. This is done through an implementation of the newly-proposed observe() API for React. The ParseReact Mixin allows a version of this new lifecycle method to be used today with Parse Queries.
If you're using React with ES6 classes, we also provide a subclass of React.Component that allows you to use the observe() and Query-specific APIs.
var CommentBlock = React.createClass({
mixins: [ParseReact.Mixin], // Enable query subscriptions
observe: function() {
// Subscribe to all Comment objects, ordered by creation date
// The results will be available at this.data.comments
return {
comments: (new Parse.Query('Comment')).ascending('createdAt')
};
},
render: function() {
// Render the text of each comment as a list item
return (
<ul>
{this.data.comments.map(function(c) {
return <li>{c.text}</li>;
})}
</ul>
);
}
});
Whenever this component mounts, it will issue the query and the results will be attached to this.data.comments. Each time the query is re-issued, or objects are modified locally that match the query, it will update itself to reflect these changes.
Mutations are dispatched in the manner of Flux Actions, allowing updates to be synchronized between many different components without requiring views to talk to each other. All of the standard Parse data mutations are supported, and you can read more about them in the Data Mutation guide.
// Create a new Comment object with some initial data
ParseReact.Mutation.Create('Comment', {
text: 'Parse <3 React'
}).dispatch();
I tried the example, but always have to reload view. It`s not the same as Firebase and MeteorJS
I would also like to hear more about this...Not sure if this is actually a supported feature or not. As the documentation states, Queries you are subscribed to in the observe function will be updated with new props/state, as well as any time a Mutation occurs. In this sense it is very much like Meteor in that changes changes to state (much like changes to Session variables) can reload queries to the backend.
Where it differs is that, unlike Meteor, changes in Parse (say, directly in the db or from another front-end instance) are not communicated to all subscribed React front-ends. At least as far as I can tell. Which is kinda disappointing. Would love to hear from someone more experienced, who hasn't just been messing with ParseReact for the past 24 hours.