How to retain state during navigation with Next.js and Redux? - redux

Using Next.js next/router will cause a reload or it will retain Redux state?
import { useRouter } from 'next/router'
const DashboardSidebar = ({ mobile = false }: Props) => {
const router = useRouter()
router.push(
`/${lang}/dashboard/organizations/${organizationId}/events/${id}`
)
Or I should use redux-persist to retain state? When I follow this setup:
https://redux-toolkit.js.org/usage/usage-guide#use-with-redux-persist
I got this error:
redux-persist failed to create sync storage. falling back to noop storage.
Do I get it because I use Next.js and SSR?
Shall I move forward and use next-redux-wrapper?
https://github.com/kirill-konshin/next-redux-wrapper

Here are some instructions: https://redux-toolkit.js.org/rtk-query/usage/server-side-rendering#server-side-rendering-with-nextjs
As the user changes routes, the store needs to be rehydrated each time before the html is generated and sent to the client.
I am not a fan of this architecture. It seems strange that the store needs to be filled back up with its state (rehydrated) every time the user needs a new render. Where are you going to persist the store in the meantime? What is its lifecyle?
In a SPA, the lifecycle of the store is when the page refreshes (which would require a programmatic invocation, or the user navigating away and coming back, or pressing Ctrl+R). Conceptually, this is much simpler.

Related

Why can't I use QueryCache to get at "cache" from queryClient.prefetchQuery

So, I am trying to access the cache that I set with: queryClient.prefetchQuery, in a SSR page.
I am using this on the SSR side that I hydrate:
ala:
await queryClient.prefetchQuery(['userSesion'], () => fetchUserSession());
// This is wrapping my _app.js file
<QueryClientProvider client={queryClient}>
<Hydrate state={props.dehydratedState}>
Then on the client side, I am trying to get at this "cache".
const queryCache = new QueryCache();
queryCache.find("userSession"); // <--- This is undefined/empty
Also tried: no luck
queryClient.getQueryData('userSession'))
queryClient.getQueryState('userSession'))
Mind you, I "SEE" the data/cache in the tools, so why can't I get at it?
because you create a new Cache, and that cache has no connection to the queryClient. If you want to get the cache, you can do this via:
const queryClient = useQueryClient()
const queryCache = queryClient.getQueryCache()
that being said, there should be rarely the need to interact with the QueryCache directly. The QueryClient exposes functions to interact with the cache, and if you have a component that needs data from the cache, it's always preferred to just call useQuery in that component. It will give you data from the cache instantly if there is any, and perform a background refetch if the data is considered stale.

Exclude redux-form reducer from main store

import { combineReducers } from 'redux'
import { reducer as form } from 'redux-form'
combineReducers({
router: connectRouter(history),
form,
....
// huge amount of other reducers
})
Above you can see combined reducers in one store. As bigger it becomes as slower it becomes because on each action it should make many more checks as it was at the beginning. As you also can see I use redux-form for handling state in my form. And it starts to be slower and slower when I type in redux-form fields. I want to find out is there any way to create some individual store for redux-form, and prevent this from slowing down. Or is there exist some other solutions to this problem?
Using redux you can have multiple stores.
redux-form is using connect API from react-redux to get info from store.
With React Redux, the wrapper classes generated by the connect() function do actually look for props.store if it exists, but it's best if you wrap your root component in and let React Redux worry about passing the store down. This way components don't need to worry about importing a store module, and isolating a Redux app or enabling server rendering is much easier to do later.
https://redux.js.org/faq/store-setup#can-or-should-i-create-multiple-stores-can-i-import-my-store-directly-and-use-it-in-components-myself
I'm a redux-form collaborator.

How can I ensure my not yet fetched data doesn't break my site

I am currently building a like button on my card component in vue. I am fetching data from firebase using middleware on a page to dispatch the vuex action that will go and get my user info which has their liked_posts stored in an array.
The issue comes up that when I load a page requiring some of the data
i.e. liked_posts and my state is empty it throws a error of
"undefined".
How can I make sure that even if the user hasn't signed in or hasn't ever visited that my user data wont cause an error
I have tried to change my action in the Vuex store to be asynchronous and use await so that I made sure the data was there, but it didn't help.
What is happening is the below code in computed properties is trying to access an object that doesn't exist in the array yet.
likedOrNot() {
const likeInfo = this.$store.state.userInfoSub[0].liked_posts
return likeInfo.includes(this.$store.state.loadedCards[0].id)
}
This data isn't there yet because the user isn't signed in, exist ect. once they do and middleware is dispatching an action to fetch the user data the userInfoSub will be filled with info.
my base state looks like this when the user hasn't signed in or middleware hasnt fired to look for the user that gets put in cookies.
So I need away to ensure my lack of userInfoSub doesn't break my computer property
loadedCards:Array[1]
0:Object
token:null
user:null
userInfoSub:Array[0]
username:null
Here's an opinionated answer: use get from lodash.
npm i lodash
Then you can write something like this:
import get from 'lodash/get';
export default {
computed: {
isLiked() {
const cardId = get(this.$store, 'state.loadedCards[0].id');
const postIds = get(this.$store, 'state.userInfoSub[0].liked_posts', []);
return postIds.includes(cardId);
},
},
};

How to create a redux store with local data storage?

I am confused by the docs. I am trying to use Redux-storage middleware and this is how they show the store is created with the middleware:
const middleware = storage.createMiddleware(engine);
const createStoreWithMiddleware = applyMiddleware(middleware)(createStore);
const store = createStoreWithMiddleware(reducer);
But the Redux docs show this:
let store = createStore(
todos,
[ 'Use Redux' ], // How do I put here the data from the redux-storage?
applyMiddleware(middleware) // here goes the redux-storage
)
createStore requires an initial store value to be passed, but I can't load the stored values without having a store. This is a kind of catch 22. What did I miss here?
You actually miss nothing and that is intended behavior of redux-storage: there is a gap between creating redux store and the moment it got filled with the stored data. That is because storage engine provided to redux-storage createLoader can have an async nature in general case.
The common pattern to deal with that is following:
Your app starts up with uninitialized store state
It shows some kind of a preloader to your user
App creates loader and loads stored state: const load = storage.createLoader(engine); load(store);
Wait store to be rehydrated and hide preloader.
There is another store peristing lib out there: redux-persist. But initialization process works the same way, except you don't have to call load explicitly.

Component is not unmount after its delete in store

Project (Todolist) was created with immutable library, source here
Store structure: project have many tasks, In redux store: State - map, projects, tasks - Records
When I asyncly remove project ...
export const removeProject = project => (dispatch) => {
if (!isProjectExist(project)) return Promise.resolve()
return projectService
.delete(project)
.then(
() => {
dispatch(remove(project))
console.log("post removeProject resolved")
},
handleError,
)
}
.... that was created after initialization - it will be deleted and properly unmounted, but when project was passed as initialState - ProjectList will not be rerendered, and ProjectItem try to render itself with stale data, and fail, as in picture
It have tests
It looks like reducer returs changed data, but I use immutablejs, and previously i use normalizr-immutable, but I thought that source of issue in this library and write my own normalizeInitialState (source), it did not help, now I think that maybe source of problem in redux-immutable
I struggled entire day on solving of this problem
creator of redux says
I don't think this is something we can fix. React state changes are
asynchronous and React may (or may not) batch them. Therefore, the
moment you press “Remove”, the Redux store updates, and both Item and
App receive the new state. Even if the App state change results in
unmounting of Items, that will happen later than mapStateToProps is
called for Item.
Unless I'm mistaken, there is nothing we can do. You have two options:
Request all required state at App (or a lower, e.g. ItemList) level
and pass it down to “dumb” Items. Add safeguards to mapStateToProps
for “currently unmounting” state. For example, you may return null
from render in this case. Potentially we could have the component
generated by connect() return null from its render if mapStateToProps
returned null. Does this make any sense? Is this too surprising?
Hm, I never saw stubs like return (<div></div>) or safeguards in mapStateToProps in others code
markerikson
I'm not entirely sure I follow what exactly your problem is, but as a
guess: it sounds like the child component is re-rendering before the
parent is. This is a known issue with React-Redux v4 and earlier. The
v5 beta fixes that issue. Try installing react-redux#next and see if
that takes care of your problem.

Resources