I tried to implement redux-persist for my SSR NextJs application without any success. I pretty much use the same code from the basic usage example https://github.com/rt2zz/redux-persist#basic-usage
As some guys of you may know the .getInitialProps() (https://nextjs.org/docs/api-reference/data-fetching/getInitialProps) function from NextJs fetches data serverside. So Redux-Persist for me only works, when I fetch the data clientside after persist/PERSIST & persist/REHYDRATE was called already.
Maybe I'm doing something wrong or did not understand the concept fully, but here is my procedure & how actions are being called from the reducer:
{type: '##redux/INITr.6.5.z.4.q'}
{type: 'myDataFetchingReducer', payload: xxxxx } -> data is fetched successfully via getInitialProps(), the redux state & store is being updated with the new data
{type: '##redux/INITr.6.5.z.4.q'} -> state & store still looks good
{type: 'persist/PERSIST'} -> state & store still looks godd
{type: 'persist/HYDRATE'} -> state & store get's reset to the state,
before myDataFetchingReducer was called
Would be cool to get some insights at this point. I don't know if I'm doing something wrong here or if my understanding of how it should work is wrong here. Thanks!
Related
Hello this is my first question. I am trying to set up a project where modules along with the redux and sagas will be injected into the main app, using redux-injectors. In my sagas I want to use yield select, to check if an action has updated the state and then carry on. For example, when I post an image, I want to make sure there were no errors in posting the file and then move on. I use the following function:
export const imageErrors = (state: RootState): IImagesErrorState => state.image.errors
and then in the saga.ts file I use it as such:
if (imagesErrors?.postImageError !== null) {
throw imagesErrors.postImageError
}
this works fine as long as the state.image exists in the root state from the beginning. However, how do I do that when I want to inject this state later on using useInjectReducer and useInjectSaga? I obviously get an error
Property 'image' does not exist on type 'Reducer<CombinedState<{ user: CombinedState<{ auth: IAuthState; errors: IErrorState; }>; }>, AnyAction>'.ts(2339)
So how do we handle selectors of specific pieces of state, since state does not yet include them?
Thank you so much.
Can't talk about the Typescript part of things, but in terms of architecture you've got two options.
One is the obvious - that is to add conditions or ? everywhere to avoid errors from accessing missing properties, but that can get tedious quickly.
The other probably better option is to rethink your state & application chunks. What is this saga that is accessing state that isn't existing yet? Does it need to run before you have such state? If not, let's move the saga to the same chunk as the reducer. In the opposite case, where you need the saga to be running e.g. as part of the runtime chunk, then perhaps the image state should be in the runtime chunk as well.
I'm new to all of these technologies, but as far as I understand it, you can use React Native with Redux and Firebase without react-redux-firebase. You could just use
react
react-native
redux
react-redux
react-native-firebase
Then you load data from Firebase (e.g. Firestore) and put the data in a reducer so it gets merged into the redux store.
Why do I need react-redux-firebase? What problem does it solve?
I have tried its docs, but they seem to be written for someone who is already familiar with its goals. They do not really explain, and when reading the examples, I do not understand why I specifically need react-redux-firebase instead of the setup listed above.
Firebase is on your state, listen to it an modify it, it will change your Firebase database. After the data on the database is changed the components listening will change as well.
This will create an item in the database
updateTodo: props => () => {
return firebase.update(`todos/${params.todoId}`, { done: !todo.isDone })
}
So any component listening to that node will get updated:
connect((state) => ({
todos: state.firebase.data.todos,
// profile: state.firebase.profile // load profile
}))
It solves the problem of having multiple sources of truth, your Firebase database is your only source of truth, otherwise, you change your local data and then you update the data online and then if it works nothing else but if it fails you have to update the local data again
I have recently begun coding in Redux.
Before Redux with AngularJS it was easy to map models with state using $localstorage. I just can figure out the best way to do that with Redux.
Should I be dispatching and action and ask reducers to read local storage for in my code ?
Or should I allow local storage to be managed with a global object ?
There are few ways.
Just note that for syncing to localStorage you need to call JSON.stringify which is quite expensive, so please don't do that often and also with large data structures as it might hurt app's performance.
1) Sync whole Redux store to Local Storage. You can use existing solution for that eg. https://github.com/elgerlambert/redux-localstorage
I would not recommend to sync whole store as you might sync also state which should not be persisted after refresh and also you might make application slower; For better performance you can use paths argument in above library or use one of another options.
To see how you can build such functionality manually, there is great explanation video from Dan https://egghead.io/lessons/javascript-redux-persisting-the-state-to-the-local-storage
2) Manually build simple cache middleware like below, which might catch specific actions you would like to sync with local storage
const cacheMiddleware = store => next => action => {
if(action.type !== 'GET_SOMETHING') {
return next(action);
}
const data = getFromLocalstorage();
if(!data) {
// Fetch and put to localstorate for later use
const data = fetchFromServer();
return next({ type: 'SERVER_RESULT', data });
}
return next({ type: 'CACHED_RESULT', data });
};
3) If you are using Redux Thunk you can perform caching there as you are allowed to have side effects in actions.
You can find more info about Redux middleware here https://redux.js.org/advanced/middleware
I'm working with Redux and my state is a normalized one with a lot of different models. Now I was wondering myself if it was better to have specific actions like:
{type: CHANGE_MODEL_NAME, modelId, name}
vs
{type: UPDATE_MODEL, modelId, {name}}
I did a bit of searching and I found this question:
Is it ok to create generic redux update action
Now what I'm wondering is that no one is adressing the fact that having specific action types allow for different reducers to 'react' to an action in a cleaner way.
IE: I have a model that is copied from another model like so:
{
name: 'foo',
originalModel: id_0
}
It then becomes easier to react to specific actions in my reducer of copied models if I only want to react to the name change action.
Is it wrong for 2 reducers to react to the same actions? Is that why nobody adressed this issue in the original question?
Having multiple slice reducers respond to the same actions is absolutely an intended use case for Redux. I covered that background in my post The Tao of Redux, Part 1 - Implementation and Intent.
As for your specific question: I think it's entirely valid to have an update action for normalized data that contains the item type name and the item ID. In fact, I demonstrated this exact approach in my post Practical Redux, Part 7: Form Change Handling, Data Editing, and Feature Reducers.
Overall, Redux itself doesn't care what specific action types you have and how generic they are. You are encouraged to define whatever actions are appropriate for your app, and what level of "abstraction" they represent. It's very reasonable to make them a bit more generic - for example, I'd prefer UPDATE_USER_ATTRIBUTES instead of SET_USER_FIRST_NAME and SET_USER_LAST_NAME, but ultimately it's up to you.
This is perfectly valid. This pattern even has a name. "Applying a change set"
Your message becomes the following:
{type: APPLY_CHANGSET, data: {id: idOfThingToApplyTo, propOne: '1', propTwo: '2'}}
Your reducers can then look like this:
const propOneReducer = (value = 'default', {type, {data: {propOne}}) => {
return type === APPLY_CHANGSET && propOne !== undefined ? propOne : value;
}
This makes it a lot easier to add new properties (attributes) to your objects in your store. Adding a reducer, and sending the data from your react views to the actionCreator. In simple cases, you might not even need to change the actionCreator.
In these simple cases you can even build a reducer creator, basically creating the reducer for you.
Pro's
Less actions in the system
Simple sweet actionCreators
Not Pro's
Actions don't describe exactly what is happening. It's also harder to parse exactly what happens to the store after a actionCreator is invoked. This because the reducers now take the shape of the data into account.
Slightly more complex reducers
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.