Redux Offline issue with rehydrating reducer to null - reactjs

I am using redux-offline, below is how I am creating my store
let customConfig = {
...offlineConfig,
persistOptions:{
key: 'root',
transforms: [immutableTransform()],
},
returnPromises : false,
persistCallback : () => {this.setState({rehydrated : true})}
};
const { middleware, enhanceReducer, enhanceStore } = createOffline(customConfig);
let middlewares = applyMiddleware(offlineCommitHandler,thunk,middleware,);
store = createStore(enhanceReducer(IndexReducer),undefined,compose(enhanceStore,middlewares,persistAutoRehydrate({log:true})));
I have multiple reducers.
The issue occurs only in rehydrating one reducer, for ex : reducerA
I placed a debugger in autoRehydrate
When opening the app first time it merges the data for reducerA
When opening the app second time inbound state for reducerA is
null.

I never did offline config but I ran into similiar problems with redux-persist.
So first of all you want to make sure that your debugger and your device/emulator are not drifting away in time. This would cause me problems all the time, simply restarting the emulator and chrome tab which I was using for debugging would solve it.
Second, you want to check if you don't have any errors in your reducer. In my case, I was not following the immutability pattern and that would sometimes cause redux not to persist my state, as it did not see any difference between the old state and the new state.

For anyone looking for answer to similar issue.
My issue was that the data i was trying to store was hitting the max storage limit for asyncStorage .
https://github.com/rt2zz/redux-persist/issues/199
I had to shift to using filestorage instead of asyncStorage to get redux persist store the data

Related

How do we persist redux store without using localstorage and make full reactjs web application work offline

I have to do full offline functionality like all redux store data should remain after page refresh/offline(network connection lost).
When user goes offline previously stored data should be used in viewing pages & for suppose i have big forms(ex: employee form with all work experience and personal details)suddenly if user loose network, data should not be lost.. should be saved somehow & after that when user is online have to store data to DB
I am using react -18.0.0 version with react-redux & redux-saga.What are the possible ways?
lets say users are required to log in to system .once login is successfull save that
data to local storage then dispatch loginsuccess with user and token as payloads
execute a function inside useEffect in app.js to get back saved data from local storage and dispatch
loginsuccess with those data
inside signup function:
let res=await axios.post("/signin",{user});
let{user,token}= res.data;
localStorage.setItem("user", JSON.stringify(user));
localStorage.setItem("token",token);
dispatch({ type: "loginsuccess",payload:{user,token}});
inside app.js :
useEffect(() => {
dispatch(isUserLoggedIn());
}, []);
inside isUserLoggedIn :
const localstorage = localStorage.getItem("user");
if (localstorage) {
const user = JSON.parse(localStorage.getItem("user"));
dispatch({ type: "loginsuccess",payload:{user}});
} else {
dispatch({ type: "loginfailed",payload:{error:"please log in"}});
}
One way can be to store your form(or state) in localstorage. Another way can be use library redux-persist .
I think your question is similar to the one asked here. Have a look here for more ideas.
Another approach to store your data, is to make use of the IndexedDB API, it already has a great support on the latest browser versions caniuse.
There is a really good library that make use of this api but
combined with promises idb.
After picking up how you're gonna do it, you can have a watcher saga that reacts to the online / offline status and do the appropriated synchronization.
Also I think it's important to mention that all of the above will only take care of the data, you still need to make sure to make the JS and CSS to be available offline. on web.dev there is a great induction on this subject.

Redux state isn't being updated across the app in development

I'm having a very weird issue with redux. In my current app, I decided to use a logout pattern suggested by Dan Abramov.
Basically, it looks like this:
const withLogout = (state, action) => {
if (action.type === UserActions.logout.toString()) {
state = undefined
}
return rootReducer(state, action)
}
The state is getting purged after the logout action is dispatched. I added logs to reducers and they all show that the state is getting reset to a default value. React Native Debugger shows the same thing.
However, the state inside the connect(react-redux) functions doesn't change. Even weirder, it looks like the function detects the fact that the state has changed because the logger function in mapState is being triggered. But the logger's output shows that the state hasn't changed there. Components are also not getting updated.
And as I mentioned in the title this issue happens only in dev mode. As soon as I build the app and install it everything starts working as intended. And it isn't caused by Debugger.
Any thoughts?
I found the reason. I had to import the store in a couple of places where I couldn't use react-redux to dispatch the action. This led to require cycles which sometimes led to creating multiple instances of the store.
I stumbled upon the following solution that makes sure you're using the same instance of the store across the app: instead of importing store in several places use the following pattern:
// somewhere in the code
let store
export const setStore = storeInstance => store = storeInstance
...
store.dispatch()
// store index.js
import { setStore } from 'someplace'
...
setStore(store)

Should I build a local data layer/app state to maintain state in a React Native/Firestore App?

A main selling point of Firestore is the ability to use it as a online/offline source of truth. I'm using it in this way right now: updating the Firestore document directly on an action, then listening to Firestore DB changes and mapping this back to local state. However, relying on this latency compensation and mapping back to local state is not sufficient for quick updates (taps, toggles even with a small document size). For example, toggles will "jitter" as the RN toggle presumptively shifts on tap, and the local state hasn't been updated until it already returns see video example. It appears worse on Android and the problem isn't strictly limited to basic toggles.
Does document size or query result size have a bigger impact on latency compensation? Our document size is very small right now with a worst case ~1000 query result set. We could make the documents 1000x bigger (100kb) and have a query result set of size 1. Update: Testing appears inconsistent here, latency compensation is not ideal in either case
Which of the following other things may impact latency compensation?
Using queries with custom indexes. Note: we're not currently reading from cache, we're using the JS SDK
Multiple writes. Would multiple writes to the same document make it worse (4 quick writes vs. 2 quick writes). Update: not clear this makes a big difference.
Using the native vs. JS module. We're currently using the Firestore Web SDK with an Expo app. Update: switching to native module via React-Native Firestore has no apparent performance improvement.
Is it common for people to build a local data shim layer / local app state with React Native / Firestore apps to help improve local performance speed? Are there any suggested libraries for this?
On app load, mount the listener, and export the result to context to be used through the app
const [user, setUser] = useState();
firebase.firestore().collection(`users/${user.uid}`).onSnapshot(qs => setUser(oldState => {
const newState = {};
qs.docChanges().forEach(change => {
if (change.type === "added" || change.type === "modified") {
newState[change.doc.id] = {
docid: change.doc.id,
...change.doc.data(),
};
} else if (change.type === "removed") {
delete oldState[change.doc.id];
}
});
return {
...oldState,
...newState,
};
}))
Sample component and function to toggle notifications: (switch is jittery)
const toggleNotifications = (user, value) => {
firebase.firestore().doc(`users/${user.uid}`).update({
wantNotifications: value,
});
};
const TestComponent = () => {
//gets from context, set in listener mounted on app load
const { user } = useUserContext();
return (
<Switch
value={user.wantNotifications}
onValueChange={value => toggleNotifications(user, value)}
/>
);
};
This not an answer, just a long comment :)
#learningAngular For example, in toggleNotifications just need to call an async action creator and don't worry about putting any logic inside react component.
Instead Redux pattern gives space to do some logics, in this case because user's last moment decision is source of truth so dispatch function would always set a local tempState and updatingStatus before start updating firestore, then after firestore promise either resolved or rejected dispatches an action to reducer to reset updatingStatus. Then a selector would check if updatingStatus is true to just rely on local tempState otherwise rely on listened firestore state. Finally, react component use selector result as currently valid state.
I am not answering this question because I don't have that much experience. I am also curious if a good solution is out there, but this is what I think is the solution at this moment.
I updated the answer with specific learnings, but after a lot of testing, my biggest general learnings so far are
Latency compensation can be very inconsistent even with the same data and environment. Listeners can take time to "warm up", as is mentioned in other questions. It is hard to have a standard metric here.
Document size DOES impact latency compensation. Everything else so far is inconclusive.

Devtools for React Easy State?

i just switcher from redux, is there any tooling available, to inspect or even manipulate the react Easy State stores for dev purpose or do you have any good practice Tipps to do so?
Is ist maybe possible to console.log the current State on every change?
We don't have a devtool yet but it is an often requested feature. It's on our agenda and we are already collecting data about what people expect from a devtool. Sooo... what are the must-have features in a React state devtool to you?
About the timeline: we will release better docs, a linter, and probably strict-mode before a devtool. We already have a very basic devtool primitive (which just logs a lot of data) that could be used in the meantime. It would never be an official API though and we would just remove it in a later release. Are you interested? Should we release it as a temporary solution?
Is ist maybe possible to console.log the current State on every change?
Sure:
import { store, autoEffect } from '#risingstack/react-easy-state'
const myStore = store({
name: 'Bob'
})
autoEffect(() => console.log(JSON.stringify(myStore, null, 2)))
// logs `{ name: 'Ann' }`
myStore.name = 'Ann'
(I am an author of React Easy State)
If you're using global stores, e.g.:
const myStore = store({
a: 1
});
You can assign them to the window object so in your chrome/firefox devtools you could do something like:
window.__EASY_STORES__ = {
MY_STORE: myStore
}
You can then mutate that object in the console and it should be reflected in the rendering if your components are wrapped in view.
Other than that there's currently discussion around building a whole suite of devtools in the community, but at the moment we don't provide any out of the box inspector or dev tooling around the library.
It is indeed possible to log state changes using middleware. A simple logging middleware can look like this (it's typed in Flow):
export default (store: Store<ReduxState>) => (
next: (action: Action) => ReduxState
) => (action: Action) => {
console.log(`dispatching: ${action.type}`)
const result = next(action)
console.log(`next state: ${JSON.stringify(store.getState())}`)
return result
}
Manipulating is another thing. You could eather create a "cli" - I've recently done this in a project. It's basically just a JS function exposed to the browsers console.
Or, what I would suggest is using a browser plugin. The most commonly known is probably "Redux DevTools" which is available at least for Firefox and Chrome. It gives you CRUD-control (create, read, update, delete) over redux-state.
Edit: Since I fataly misread your question, this comment on GitHub might interest you. Seems not to have very active maintainers^^ But sorry, I don't know anything about easy-state.

How to restore cache to its initial state

I use SSR to rehydrate the cache with initial state from the server
const cache = new InMemoryCache().restore(window.__APOLLO_STATE__)
Few mutations later, I need to hard reset the cache to its initial state. I naively tried to keep the initial store in component state and pass it to client.restore
componentDidMount () {
this.setState({
initState: this.props.client.extract()
})
}
// When I need to reset, I'd call this method...
handleCacheReset () {
this.props.client.restore( this.state.initState )
}
but it didn't work. Since I'm also using using apollo-link-state, I thought if I keep the cache data as defaults, maybe then I can write them when client.resetStore is called
const cache = new InMemoryCache().restore(window.__APOLLO_STATE__)
const link = withClientState({ cache })
const client = new ApolloClient({
link, cache,
defaults: cache.data.data
})
client.onResetStore(link.writeDefaults)
// ...when it's time to reset the store
client.resetStore()
But I only get Missing field ... in {} errors and no data gets written to the store. Worse, I can't use client.writeData since I only have the initial window.__APOLLO_STATE__. If only I could read the entire cache data, then it might be plausible, but there doesn't seem to be a readData method.
Is there a way to programmatically restore the cache to its original state?
So far the only workaround I see is re-creating the entire ApolloClient and forcefully re-rendering the tree, which is expensive and inefficient. Thoughts?
P.S. Asked this on GitHub, but they closed it with no answer.
Is this on web or mobile? I haven't been able to solve this myself, but in thinking about apollo-cache-persist, you could theoretically save states of your cache to local storage and restore them to your cache at a later time.
this.props.client.extract() returns directly the cache object not a copy :
https://github.com/apollographql/apollo-client/blob/master/packages/apollo-cache-inmemory/src/depTrackingCache.ts#L23
If you want to keep the original state, you can do :
initState: {...this.props.client.extract()}

Resources